pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
WIP: BRIN multi-range indexes
177+ messages / 20 participants
[nested] [flat]

Commits from this thread:
  2021-03-26 ab596105b5  BRIN minmax-multi indexes
  2021-03-26 77b88cd1bb  BRIN bloom indexes
  2021-03-26 a681e3c107  Support the old signature of BRIN consistent function
  2021-03-22 8e4b332e88  Optimize allocations in bringetbitmap
  2021-03-22 72ccf55cb9  Move IS [NOT] NULL handling from BRIN support functions
  2021-03-22 a1c649d889  Pass all scan keys to BRIN consistent function at once
Related threads (commitfest entry BRIN multi-minmax and bloom indexes/2523):
  WIP: BRIN bloom indexes
* WIP: BRIN multi-range indexes
@ 2017-10-25 16:22 Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2017-10-25 16:22 UTC (permalink / raw)
  To: pgsql-hackers

Hi all,

A couple of days ago I've shared a WIP patch [1] implementing BRIN
indexes based on bloom filters. One inherent limitation of that approach
is that it can only support equality conditions - that's perfectly fine
in many cases (e.g. with UUIDs it's rare to use range queries, etc.).

[1]
https://www.postgresql.org/message-id/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com

But in other cases that restriction is pretty unacceptable, e.g. with
timestamps that are queried mostly using range conditions. A common
issue is that while the data is initially well correlated (giving us
nice narrow min/max ranges in the BRIN index), this degrades over time
(typically due to DELETE/UPDATE and then new rows routed to free space).
There are not many options to prevent this, and fixing it pretty much
requires CLUSTER on the table.

This patch addresses this by BRIN indexes with more complex "summary".
Instead of keeping just a single "minmax interval", we maintain a list
of "minmax intervals", which allows us to track "gaps" in the data.

To illustrate the improvement, consider this table:

    create table a (val float8) with (fillfactor = 90);
    insert into a select i::float from generate_series(1,10000000) s(i);
    update a set val = 1 where random() < 0.01;
    update a set val = 10000000 where random() < 0.01;

Which means the column 'val' is almost perfectly correlated with the
position in the table (which would be great for BRIN minmax indexes),
but then 1% of the values is set to 1 and 10.000.000. That means pretty
much every range will be [1,10000000], which makes this BRIN index
mostly useless, as illustrated by these explain plans:

    create index on a using brin (val) with (pages_per_range = 16);

    explain analyze select * from a where val = 100;
                                  QUERY PLAN
    --------------------------------------------------------------------
     Bitmap Heap Scan on a  (cost=54.01..10691.02 rows=8 width=8)
                            (actual time=5.901..785.520 rows=1 loops=1)
       Recheck Cond: (val = '100'::double precision)
       Rows Removed by Index Recheck: 9999999
       Heap Blocks: lossy=49020
       ->  Bitmap Index Scan on a_val_idx
             (cost=0.00..54.00 rows=3400 width=0)
             (actual time=5.792..5.792 rows=490240 loops=1)
             Index Cond: (val = '100'::double precision)
     Planning time: 0.119 ms
     Execution time: 785.583 ms
    (8 rows)

    explain analyze select * from a where val between 100 and 10000;
                                  QUERY PLAN
    ------------------------------------------------------------------
     Bitmap Heap Scan on a  (cost=55.94..25132.00 rows=7728 width=8)
                      (actual time=5.939..858.125 rows=9695 loops=1)
       Recheck Cond: ((val >= '100'::double precision) AND
                      (val <= '10000'::double precision))
       Rows Removed by Index Recheck: 9990305
       Heap Blocks: lossy=49020
       ->  Bitmap Index Scan on a_val_idx
             (cost=0.00..54.01 rows=10200 width=0)
             (actual time=5.831..5.831 rows=490240 loops=1)
             Index Cond: ((val >= '100'::double precision) AND
                          (val <= '10000'::double precision))
     Planning time: 0.139 ms
     Execution time: 871.132 ms
    (8 rows)

Obviously, the queries do scan the whole table and then eliminate most
of the rows in "Index Recheck". Decreasing pages_per_range does not
really make a measurable difference in this case - it eliminates maybe
10% of the rechecks, but most pages still have very wide minmax range.

With the patch, it looks about like this:

    create index on a using brin (val float8_minmax_multi_ops)
                            with (pages_per_range = 16);

    explain analyze select * from a where val = 100;
                                  QUERY PLAN
    -------------------------------------------------------------------
     Bitmap Heap Scan on a  (cost=830.01..11467.02 rows=8 width=8)
                            (actual time=7.772..8.533 rows=1 loops=1)
       Recheck Cond: (val = '100'::double precision)
       Rows Removed by Index Recheck: 3263
       Heap Blocks: lossy=16
       ->  Bitmap Index Scan on a_val_idx
             (cost=0.00..830.00 rows=3400 width=0)
             (actual time=7.729..7.729 rows=160 loops=1)
             Index Cond: (val = '100'::double precision)
     Planning time: 0.124 ms
     Execution time: 8.580 ms
    (8 rows)


    explain analyze select * from a where val between 100 and 10000;
                                 QUERY PLAN
    ------------------------------------------------------------------
     Bitmap Heap Scan on a  (cost=831.94..25908.00 rows=7728 width=8)
                        (actual time=9.318..23.715 rows=9695 loops=1)
       Recheck Cond: ((val >= '100'::double precision) AND
                      (val <= '10000'::double precision))
       Rows Removed by Index Recheck: 3361
       Heap Blocks: lossy=64
       ->  Bitmap Index Scan on a_val_idx
             (cost=0.00..830.01 rows=10200 width=0)
             (actual time=9.274..9.274 rows=640 loops=1)
             Index Cond: ((val >= '100'::double precision) AND
                          (val <= '10000'::double precision))
     Planning time: 0.138 ms
     Execution time: 36.100 ms
    (8 rows)

That is, the timings drop from 785ms/871ms to 9ms/36s. The index is a
bit larger (1700kB instead of 150kB), but it's still orders of
magnitudes smaller than btree index (which is ~214MB in this case).

The index build is slower than the regular BRIN indexes (about
comparable to btree), but I'm sure it can be significantly improved.
Also, I'm sure it's not bug-free.

Two additional notes:

1) The patch does break the current BRIN indexes, because it requires
passing all SearchKeys to the "consistent" BRIN function at once
(otherwise we couldn't eliminate individual intervals in the summary),
while currently the BRIN only deals with one SearchKey at a time. And I
haven't modified the existing brin_minmax_consistent() function (yeah,
I'm lazy, but this should be enough for interested people to try it out
I believe).

2) It only works with float8 (and also timestamp data types) for now,
but it should be straightforward to add support for additional data
types. Most of that will be about adding catalog definitions anyway.


regards


-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments:

brin-multi-range-v1.patch (text/x-patch, 19.7K) download diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 6ec3297..94d696e 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -29,9 +29,6 @@ typedef struct MinmaxMultiOpaque FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; } MinmaxMultiOpaque; -#define MODE_VALUES 1 -#define MODE_RANGES 2 - #define MINMAX_MAX_VALUES 64 typedef struct MinmaxMultiRanges @@ -39,12 +36,13 @@ typedef struct MinmaxMultiRanges /* varlena header (do not touch directly!) */ int32 vl_len_; - int mode; /* values or ranges in the data array */ + /* maxvalues >= (2*nranges + nvalues) */ int maxvalues; /* maximum number of values in the buffer */ - int numvalues; /* number of values in the data buffer */ + int nranges; /* number of ranges stored in the array */ + int nvalues; /* number of values in the data array */ /* values stored for this range - either raw values, or ranges */ - Datum data[FLEXIBLE_ARRAY_MEMBER]; + Datum values[FLEXIBLE_ARRAY_MEMBER]; } MinmaxMultiRanges; @@ -68,13 +66,11 @@ minmax_multi_init(int maxvalues) /* * Allocate the range list with space for the max number of values. */ - len = offsetof(MinmaxMultiRanges, data) + maxvalues * sizeof(Datum); + len = offsetof(MinmaxMultiRanges, values) + maxvalues * sizeof(Datum); ranges = (MinmaxMultiRanges *) palloc0(len); - ranges->mode = MINMAX_MAX_VALUES; ranges->maxvalues = maxvalues; - ranges->mode = MODE_VALUES; /* start by accumulating values */ SET_VARSIZE(ranges, len); @@ -87,6 +83,12 @@ typedef struct compare_context Oid colloid; } compare_context; +typedef struct DatumRange { + Datum minval; + Datum maxval; + bool collapsed; +} DatumRange; + static int compare_values(const void *a, const void *b, void *arg) { @@ -109,27 +111,77 @@ compare_values(const void *a, const void *b, void *arg) return 0; } +static int +compare_ranges(const void *a, const void *b, void *arg) +{ + DatumRange ra = *(DatumRange *)a; + DatumRange rb = *(DatumRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + 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)) + return 1; + + return 0; +} + +/* static void print_range(char * label, int numvalues, Datum *values) { - int i; + int idx; StringInfoData str; initStringInfo(&str); - for (i = 0; i < (numvalues/2); i++) + idx = 0; + while (idx < 2*ranges->nranges) + { + if (idx == 0) + appendStringInfoString(&str, "RANGES: ["); + else + appendStringInfoString(&str, ", "); + + appendStringInfo(&str, "%d => [%.9f, %.9f]", idx/2, DatumGetFloat8(values[idx]), DatumGetFloat8(values[idx+1])); + + idx += 2; + } + + if (ranges->nranges > 0) + appendStringInfoString(&str, "]"); + + if ((ranges->nranges > 0) && (ranges->nvalues > 0)) + appendStringInfoString(&str, " "); + + while (idx < 2*ranges->nranges + ranges->nvalues) { - if (i > 0) + if (idx == 2*ranges->nranges) + appendStringInfoString(&str, "VALUES: ["); + else appendStringInfoString(&str, ", "); - appendStringInfo(&str, "\n\t%d => [%.9f, %.9f]", i, DatumGetFloat8(values[2*i]), DatumGetFloat8(values[2*i+1])); + appendStringInfo(&str, "%.9f", DatumGetFloat8(values[idx])); + + idx++; } + if (ranges->nvalues > 0) + appendStringInfoString(&str, "]"); + elog(WARNING, "%s : %s", label, str.data); resetStringInfo(&str); pfree(str.data); } +*/ /* * minmax_multi_contains_value @@ -143,33 +195,18 @@ minmax_multi_contains_value(BrinDesc *bdesc, Oid colloid, int i; FmgrInfo *cmpFn; - Datum a, b; - - memcpy(&a, &ranges->data[0], sizeof(Datum)); - memcpy(&b, &ranges->data[ranges->numvalues-1], sizeof(Datum)); - /* - * If we're still collecting exact values, let's see if we have an - * exact match by using equality strategy. + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. */ - if (ranges->mode == MODE_VALUES) + if (ranges->nranges > 0) { - cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, - BTEqualStrategyNumber); + Datum compar; + bool match = true; - for (i = 0; i < ranges->numvalues; i++) - { - Datum compar - = FunctionCall2Coll(cmpFn, colloid, newval, ranges->data[i]); - - /* found an exact match */ - if (DatumGetBool(compar)) - return true; - } - } - else /* MODE_RANGES */ - { - Datum compar; + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; /* * Otherwise, need to compare the new value with boundaries of all @@ -178,36 +215,36 @@ minmax_multi_contains_value(BrinDesc *bdesc, Oid colloid, */ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, BTLessStrategyNumber); - compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->data[0]); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); /* smaller than the smallest value in the range list */ if (DatumGetBool(compar)) - return false; + match = false; /* * And now compare it to the existing maximum (last value in the - * data array). + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. */ - cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, - BTGreaterStrategyNumber); - compar = FunctionCall2Coll(cmpFn, colloid, newval, - ranges->data[ranges->numvalues-1]); - if (DatumGetBool(compar)) - return false; + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } /* - * So it's in the general range, but is it actually covered by - * any of the ranges? Each range uses two values. + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. */ - for (i = 0; i < (ranges->numvalues/2); i++) + for (i = 0; i < ranges->nranges && match; i++) { - Datum minvalue = 0; - Datum maxvalue = 0; - Datum compar; - /* copy the min/max values from the ranges */ - minvalue = ranges->data[2*i]; - maxvalue = ranges->data[2*i+1]; + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; /* * Otherwise, need to compare the new value with boundaries of all @@ -235,7 +272,22 @@ minmax_multi_contains_value(BrinDesc *bdesc, Oid colloid, } } - /* the value is not covered by the BRIN tuple */ + /* so we're done with the ranges, now let's inspect the exact values */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ return false; } @@ -250,110 +302,207 @@ minmax_multi_add_value(BrinDesc *bdesc, Oid colloid, MinmaxMultiRanges *ranges, Datum newval) { int i; - Datum values[MINMAX_MAX_VALUES+2]; - int nvalues; - double mindistance; - int minidx; /* context for sorting */ compare_context cxt; + Assert(ranges->maxvalues >= 2*ranges->nranges + ranges->nvalues); + /* - * If we're still collecting exact values, let's see if there's a bit - * more space for one additional one. + * If there's space in the values array, copy it in and we're done. + * + * If we get duplicates, it doesn't matter as we'll deduplicate the + * values later. */ - if (ranges->mode == MODE_VALUES) + if (ranges->maxvalues > 2*ranges->nranges + ranges->nvalues) { - /* yep, there's free space, so let's just squash it in */ - if (ranges->numvalues < ranges->maxvalues) - { - ranges->data[ranges->numvalues++] = newval; - return; - } + ranges->values[2*ranges->nranges + ranges->nvalues] = newval; + ranges->nvalues++; + return; + } + /* + * There's not enough space, so try deduplicating the values array, + * including the new value. + * + * XXX maybe try deduplicating using memcmp first, instead of using + * the (possibly) fairly complex/expensive comparator. + * + * XXX The if is somewhat unnecessary, because nvalues is always >= 0 + * so we do this always. + */ + if (ranges->nvalues >= 0) + { + FmgrInfo *cmpFn; + int nvalues = ranges->nvalues + 1; /* space for newval */ + Datum *values = palloc(sizeof(Datum) * nvalues); + int idx; + DatumRange *ranges_tmp; + int nranges; + int count; + + /* sort the values */ cxt.colloid = colloid; cxt.cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); + /* copy the existing value and the new value too */ + memcpy(values, &ranges->values[2*ranges->nranges], ranges->nvalues * sizeof(Datum)); + values[ranges->nvalues] = newval; + + /* the actual sort of all the values */ + qsort_arg(values, nvalues, sizeof(Datum), compare_values, (void *) &cxt); + + /* equality for duplicate detection */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + /* keep the first value */ + idx = 1; + for (i = 1; i < nvalues; i++) + { + Datum compar; + + /* is this a new value (different from the previous one)? */ + compar = FunctionCall2Coll(cmpFn, colloid, values[i-1], values[i]); + + /* not equal, we have to store it */ + if (!DatumGetBool(compar)) + values[idx++] = values[i]; + } + /* - * Nope, we have to switch to list of ranges. We simply sort the - * values and set the mode to MODE_RANGES. + * Have we managed to reduce the number of values? if yes, we can just + * copy it back and we're done. */ - qsort_arg(ranges->data, ranges->numvalues, sizeof(Datum), - compare_values, (void *) &cxt); + if (idx < nvalues) + { + memcpy(&ranges->values[2*ranges->nranges], values, idx * sizeof(Datum)); + ranges->nvalues = idx; + pfree(values); + return; + } - ranges->mode = MODE_RANGES; - } + Assert(idx == nvalues); - Assert(ranges->mode == MODE_RANGES); - Assert(ranges->maxvalues == ranges->numvalues); + /* + * Nope, that didn't work, we have to merge some of the ranges. To do + * that we'll turn the values to "collapsed" ranges (min==max), and + * then merge a bunch of "closest ranges to cut the space requirements + * in half. + * + * XXX Do a merge sort, instead of just using qsort. + */ + nranges = (ranges->nranges + nvalues); + ranges_tmp = palloc0(sizeof(DatumRange) * nranges); - // print_range("START", ranges->numvalues, ranges->data); + idx = 0; - /* - * In the range mode we're guaranteed to have a full data array, because - * that's the only situation we switch to MODE_RANGES. So we need to - * decide which two ranges to merge, to reduce the number of ranges - * to fit back into the BRIN tuple. - * - * We do that by treating the new value as a collapsed range, and then - * merge the two ranges closest to each other. - */ + /* ranges */ + for (i = 0; i < ranges->nranges; i++) + { + ranges_tmp[idx].minval = ranges->values[2*i]; + ranges_tmp[idx].maxval = ranges->values[2*i+1]; + ranges_tmp[idx].collapsed = false; + idx++; + } - nvalues = ranges->numvalues; - memcpy(values, ranges->data, ranges->numvalues * sizeof(Datum)); + /* values as collapsed ranges */ + for (i = 0; i < nvalues; i++) + { + ranges_tmp[idx].minval = values[i]; + ranges_tmp[idx].maxval = values[i]; + ranges_tmp[idx].collapsed = true; + idx++; + } - /* add the new value as a collapsed range (that's why we add it twice) */ - memcpy(&values[nvalues++], &newval, sizeof(Datum)); - memcpy(&values[nvalues++], &newval, sizeof(Datum)); + Assert(idx == nranges); - /* sort the data again */ - cxt.colloid = colloid; - cxt.cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, - BTLessStrategyNumber); + /* sort the ranges */ + qsort_arg(ranges_tmp, nranges, sizeof(DatumRange), compare_ranges, (void *) &cxt); - qsort_arg(values, nvalues, sizeof(Datum), compare_values, (void *) &cxt); + /* Now combine as many ranges until the number of values to store + * gets to half of MINMAX_MAX_VALUES. The collapsed ranges will be + * stored as a single value. + */ + count = ranges->nranges * 2 + nvalues; - // print_range("MIDDLE", nvalues, values); + while (count > MINMAX_MAX_VALUES/2) + { + int minidx = 0; + double mindistance = DatumGetFloat8(ranges_tmp[1].minval) - DatumGetFloat8(ranges_tmp[0].maxval); - /* - * now look for the two ranges closest to each other - * - * FIXME This simply uses DatumGetFloat8, which works for timestamps, - * but we need to support custom fuctions too. - */ - minidx = 0; - mindistance = DatumGetFloat8(values[2]) - DatumGetFloat8(values[1]); + /* pick the two closest ranges */ + for (i = 1; i < (nranges-1); i++) + { + double distance = DatumGetFloat8(ranges_tmp[i+1].minval) - DatumGetFloat8(ranges_tmp[i-1].maxval); + if (distance < mindistance) + { + mindistance = distance; + minidx = i; + } + } - /* extra range at the end, for the collapsed range we added */ - for (i = 0; i < (ranges->maxvalues/2); i++) - { - double distance - = DatumGetFloat8(values[2*(i+1)]) - DatumGetFloat8(values[2*(i+1)-1]); + /* + * Update the count of Datum values we need to store, depending + * on what type of ranges we merged. + * + * 2 - when both ranges are 'regular' + * 1 - when regular + collapsed + * 0 - when both collapsed + */ + if (!ranges_tmp[minidx].collapsed && !ranges_tmp[minidx+1].collapsed) /* both regular */ + count -= 2; + else if (!ranges_tmp[minidx].collapsed || !ranges_tmp[minidx+1].collapsed) /* one regular */ + count -= 1; - if (distance < mindistance) - { - mindistance = distance; - minidx = i; - } - } + /* + * combine the two selected ranges, the new range is definiely + * not collapsed + */ + ranges_tmp[minidx].maxval = ranges_tmp[minidx+1].maxval; + ranges_tmp[minidx].collapsed = false; - memset(ranges->data, 0, ranges->maxvalues * sizeof(Datum)); + for (i = minidx+1; i < nranges-1; i++) + ranges_tmp[i] = ranges_tmp[i+1]; - /* - * Now we know which ranges to merge, which we do simply while copying - * the data back into the range list. - */ - memcpy(ranges->data, values, (2*minidx + 1) * sizeof(Datum)); + nranges--; - // print_range("END1", ranges->numvalues, ranges->data); + /* + * we can never get zero values + * + * XXX Actually we should never get below (MINMAX_MAX_VALUES/2 - 1) + * values or so. + */ + Assert(count > 0); + } - memcpy(ranges->data + (2*minidx + 1), &values[2*minidx+3], (ranges->maxvalues - 2*minidx-1) * sizeof(Datum)); + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < nranges; i++) + { + if (!ranges_tmp[i].collapsed) + { + ranges->values[2*ranges->nranges ] = ranges_tmp[i].minval; + ranges->values[2*ranges->nranges + 1] = ranges_tmp[i].maxval; + ranges->nranges++; + } + } - // print_range("END2", ranges->numvalues, ranges->data); + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < nranges; i++) + { + if (ranges_tmp[i].collapsed) + { + ranges->values[2*ranges->nranges + ranges->nvalues] = ranges_tmp[i].minval; + ranges->nvalues++; + } + } - Assert(minmax_multi_contains_value(bdesc, colloid, attno, attr->atttypid, - ranges, newval)); + pfree(ranges_tmp); + pfree(values); + } } @@ -439,7 +588,7 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) /* */ minmax_multi_add_value(bdesc, colloid, attno, attr, ranges, newval); - PG_RETURN_BOOL(updated); + PG_RETURN_BOOL(true); } /* @@ -462,6 +611,7 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) MinmaxMultiRanges *ranges; int keyno; int rangeno; + int i; /* * First check if there are any IS NULL scan keys, and if we're @@ -512,10 +662,10 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) ranges = (MinmaxMultiRanges *) DatumGetPointer(column->bv_values[0]); /* inspect the ranges, and for each one evaluate the scan keys */ - for (rangeno = 0; rangeno < (ranges->maxvalues/2); rangeno++) + for (rangeno = 0; rangeno < ranges->nranges; rangeno++) { - Datum minval = ranges->data[2*rangeno]; - Datum maxval = ranges->data[2*rangeno+1]; + 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; @@ -543,11 +693,39 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) break; case BTEqualStrategyNumber: - /* FIXME this inspects the whole range again ... */ - matches = minmax_multi_contains_value(bdesc, colloid, attno, - subtype, ranges, value); - break; + { + 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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, @@ -577,6 +755,60 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) PG_RETURN_DATUM(BoolGetDatum(true)); } + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, value, val); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + PG_RETURN_DATUM(BoolGetDatum(false)); }
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-10-25 16:51 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2017-10-25 16:51 UTC (permalink / raw)
  To: pgsql-hackers

Apparently I've managed to botch the git format-patch thing :-( Attached
are both patches (the first one adding BRIN bloom indexes, the other one
adding the BRIN multi-range). Hopefully I got it right this time ;-)

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments: download all (2 files, .tar.gz)

0001-brin-bloom-v1.patch (text/x-patch, 62.5K) download From c27f02d2839e08ebcee852448ed3838c8932d2ea Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Mon, 23 Oct 2017 22:48:58 +0200 Subject: [PATCH 1/2] brin bloom v1 --- doc/src/sgml/brin.sgml | 236 ++++++++++ src/backend/access/brin/Makefile | 2 +- src/backend/access/brin/brin_bloom.c | 727 +++++++++++++++++++++++++++++++ src/include/catalog/pg_amop.h | 59 +++ src/include/catalog/pg_amproc.h | 153 +++++++ src/include/catalog/pg_opclass.h | 25 ++ src/include/catalog/pg_opfamily.h | 20 + src/include/catalog/pg_proc.h | 10 + src/test/regress/expected/opr_sanity.out | 3 +- 9 files changed, 1233 insertions(+), 2 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index b7483df..49d53e1 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -118,6 +118,13 @@ </thead> <tbody> <row> + <entry><literal>abstime_bloom_ops</literal></entry> + <entry><type>abstime</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>abstime_minmax_ops</literal></entry> <entry><type>abstime</type></entry> <entry> @@ -129,6 +136,13 @@ </entry> </row> <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> <entry> @@ -180,6 +194,13 @@ </entry> </row> <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> <entry> @@ -191,6 +212,13 @@ </entry> </row> <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> <entry> @@ -202,6 +230,13 @@ </entry> </row> <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> <entry> @@ -213,6 +248,13 @@ </entry> </row> <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> <entry> @@ -224,6 +266,13 @@ </entry> </row> <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> <entry> @@ -235,6 +284,13 @@ </entry> </row> <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> <entry> @@ -258,6 +314,13 @@ </entry> </row> <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> <entry> @@ -269,6 +332,13 @@ </entry> </row> <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> <entry> @@ -280,6 +350,13 @@ </entry> </row> <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> <entry> @@ -291,6 +368,13 @@ </entry> </row> <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> <entry> @@ -302,6 +386,13 @@ </entry> </row> <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> <entry> @@ -313,6 +404,13 @@ </entry> </row> <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> <entry> @@ -324,6 +422,13 @@ </entry> </row> <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> <entry> @@ -335,6 +440,13 @@ </entry> </row> <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> <entry> @@ -366,6 +478,13 @@ </entry> </row> <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> <entry> @@ -377,6 +496,13 @@ </entry> </row> <row> + <entry><literal>reltime_bloom_ops</literal></entry> + <entry><type>reltime</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>reltime_minmax_ops</literal></entry> <entry><type>reltime</type></entry> <entry> @@ -388,6 +514,13 @@ </entry> </row> <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> <entry> @@ -399,6 +532,13 @@ </entry> </row> <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> <entry> @@ -410,6 +550,13 @@ </entry> </row> <row> + <entry><literal>tid_bloom_ops</literal></entry> + <entry><type>tid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>tid_minmax_ops</literal></entry> <entry><type>tid</type></entry> <entry> @@ -421,6 +568,13 @@ </entry> </row> <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> <entry> @@ -432,6 +586,13 @@ </entry> </row> <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> <entry> @@ -443,6 +604,13 @@ </entry> </row> <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> <entry> @@ -454,6 +622,13 @@ </entry> </row> <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> <entry> @@ -465,6 +640,13 @@ </entry> </row> <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> <entry> @@ -814,6 +996,60 @@ typedef struct BrinOpcInfo </para> <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table">. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. The minmax operator class requires a full set of operators to be diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 5aef925..a76d927 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,6 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000..c08f686 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,727 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 4 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +#define BLOOM_PHASE_SORTED 1 +#define BLOOM_PHASE_HASH 2 + +/* how many hashes to accumulate before hashing */ +#define BLOOM_MAX_UNSORTED 32 +#define BLOOM_GROW_BYTES 32 +#define BLOOM_NDISTINCT 1000 /* number of distinct values */ +#define BLOOM_ERROR_RATE 0.05 /* 2% false positive rate */ + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* global bloom filter parameters */ + uint32 phase; /* phase (initially SORTED, then HASH) */ + uint32 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (optimal) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* fields used only in the EXACT phase */ + uint32 nvalues; /* number of hashes stored (sorted + extra) */ + uint32 nsorted; /* number of uint32 hashes in sorted part */ + + /* bitmap of the bloom filter */ + char bitmap[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + /* https://en.wikipedia.org/wiki/Bloom_filter */ + int m; /* number of bits */ + int k; /* number of hash functions */ + + Assert((ndistinct > 0) && (ndistinct < 10000)); /* 10k is mostly arbitrary limit */ + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + k = round(log(2.0) * m / ndistinct); + + elog(DEBUG1, "create bloom filter m=%d k=%d", m, k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + */ + len = Max(offsetof(BloomFilter, bitmap), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->phase = BLOOM_PHASE_SORTED; + filter->nhashes = k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * XXX Actually, we don't need to do repalloc - we just need to set the + * varlena header length! + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(filter->phase == BLOOM_PHASE_SORTED); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) palloc(filter->nvalues * sizeof(uint32)); + + /* copy the data, then reset the bitmap */ + memcpy(values, filter->bitmap, filter->nvalues * sizeof(uint32)); + memset(filter->bitmap, 0, filter->nvalues * sizeof(uint32)); + + /* FIXME optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if a new value, keep it */ + if (values[i] != values[i-1]) + { + values[nvalues] = values[i]; + nvalues++; + } + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + memcpy(filter->bitmap, values, nvalues * sizeof(uint32)); + + pfree(values); +} + +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter); + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + + /* assume 'not updated' by default */ + if (updated) + *updated = false; + + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter,bitmap) + (filter->nvalues+1) * sizeof(uint32); + + if (len < need) + { + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts anyway. + * + * XXX Zero the newly allocated part. Maybe not really needed? + */ + filter = (BloomFilter *) repalloc(filter, len + BLOOM_GROW_BYTES); + memset((char *)filter + len, 0, BLOOM_GROW_BYTES); + SET_VARSIZE(filter, len + BLOOM_GROW_BYTES); + } + + /* copy the data into the bitmap */ + memcpy(&filter->bitmap[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the regular hashing phase */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + /* we're in the ah */ + for (i = 0; i < filter->nhashes; i++) + { + /* + * Our "hash functions" are effectively hashes of the original + * hash, with different seeds. + */ + uint32 h = ((uint32)DatumGetInt64(hash_uint32_extended(value, i))) % filter->nbits; + + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + { + filter->bitmap[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + elog(WARNING, "switching %p to hashing (sorted %d, total %d)", filter, filter->nsorted, filter->nvalues); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, bitmap) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->phase = BLOOM_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->bitmap; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + int i; + uint32 *values = (uint32 *)filter->bitmap; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + if (value == values[i]) + return true; + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + for (i = 0; i < filter->nhashes; i++) + { + /* compute the hashes with a seed, used for the bloom filter */ + uint32 h = ((uint32)DatumGetInt64(hash_uint32_extended(value, i))) % filter->nbits; + + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + return false; + } + + /* all hashes found in bloom filter */ + return true; +} + +/* + * bloom_filter_count + * Estimate the number of distinct values stored in the bloom filter. + */ +static int +bloom_filter_count(BloomFilter *filter) +{ + if (filter->phase == BLOOM_PHASE_SORTED) + return (filter->nvalues + filter->nsorted); + else + return ceil(- (filter->nbits * 1.0 / filter->nhashes * log(1 - filter->nbits_set * 1.0 / filter->nbits))); +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store a the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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_GETARG_DATUM(3); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(BLOOM_NDISTINCT, BLOOM_ERROR_RATE); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) DatumGetPointer(column->bv_values[0]); + + elog(DEBUG1, "brin_bloom_add_value: filter=%p bits=%d hashes=%d values=%d set=%d estimate=%d", + filter, filter->nbits, filter->nhashes, filter->nvalues, filter->nbits_set, + bloom_filter_count(filter)); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + column->bv_values[0] = PointerGetDatum(bloom_add_value(filter, hashValue, &updated)); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_consistent(PG_FUNCTION_ARGS) +{ + BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); + BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); + ScanKey key = (ScanKey) PG_GETARG_POINTER(2); + Oid colloid = PG_GET_COLLATION(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + PG_RETURN_BOOL(true); + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + PG_RETURN_BOOL(!column->bv_allnulls); + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + + /* if the range is all empty, it cannot possibly be consistent */ + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + filter = (BloomFilter *) DatumGetPointer(column->bv_values[0]); + + Assert(filter); + + attno = key->sk_attno; + value = key->sk_argument; + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches = bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_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); + AttrNumber attno; + Form_pg_attribute attr; + + Assert(col_a->bv_attno == col_b->bv_attno); + + /* Adjust "hasnulls" */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do */ + if (col_b->bv_allnulls) + PG_RETURN_VOID(); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * Adjust "allnulls". If A doesn't have values, just copy the values from + * B into A, and we're done. We cannot run the operators in this case, + * because values in A might contain garbage. Note we already established + * that B contains values. + */ + if (col_a->bv_allnulls) + { + col_a->bv_allnulls = false; + col_a->bv_values[0] = datumCopy(col_b->bv_values[0], + attr->attbyval, attr->attlen); + PG_RETURN_VOID(); + } + + /* FIXME merge the two bloom filters */ + elog(WARNING, "FIXME: merge bloom filters"); + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index f850be4..ef5b692 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -894,18 +894,24 @@ DATA(insert ( 4064 17 17 2 s 1958 3580 0 )); DATA(insert ( 4064 17 17 3 s 1955 3580 0 )); DATA(insert ( 4064 17 17 4 s 1960 3580 0 )); DATA(insert ( 4064 17 17 5 s 1959 3580 0 )); +/* bloom bytea */ +DATA(insert ( 5021 17 17 1 s 1955 3580 0 )); /* minmax "char" */ DATA(insert ( 4062 18 18 1 s 631 3580 0 )); DATA(insert ( 4062 18 18 2 s 632 3580 0 )); DATA(insert ( 4062 18 18 3 s 92 3580 0 )); DATA(insert ( 4062 18 18 4 s 634 3580 0 )); DATA(insert ( 4062 18 18 5 s 633 3580 0 )); +/* bloom "char" */ +DATA(insert ( 5022 18 18 1 s 92 3580 0 )); /* minmax name */ DATA(insert ( 4065 19 19 1 s 660 3580 0 )); DATA(insert ( 4065 19 19 2 s 661 3580 0 )); DATA(insert ( 4065 19 19 3 s 93 3580 0 )); DATA(insert ( 4065 19 19 4 s 663 3580 0 )); DATA(insert ( 4065 19 19 5 s 662 3580 0 )); +/* bloom name */ +DATA(insert ( 5023 19 19 1 s 93 3580 0 )); /* minmax integer */ DATA(insert ( 4054 20 20 1 s 412 3580 0 )); DATA(insert ( 4054 20 20 2 s 414 3580 0 )); @@ -952,6 +958,16 @@ DATA(insert ( 4054 23 20 2 s 80 3580 0 )); DATA(insert ( 4054 23 20 3 s 15 3580 0 )); DATA(insert ( 4054 23 20 4 s 82 3580 0 )); DATA(insert ( 4054 23 20 5 s 76 3580 0 )); +/* bloom integer */ +DATA(insert ( 5024 20 20 1 s 410 3580 0 )); +DATA(insert ( 5024 20 21 1 s 1868 3580 0 )); +DATA(insert ( 5024 20 23 1 s 416 3580 0 )); +DATA(insert ( 5024 21 21 1 s 94 3580 0 )); +DATA(insert ( 5024 21 20 1 s 1862 3580 0 )); +DATA(insert ( 5024 21 23 1 s 532 3580 0 )); +DATA(insert ( 5024 23 23 1 s 96 3580 0 )); +DATA(insert ( 5024 23 21 1 s 533 3580 0 )); +DATA(insert ( 5024 23 20 1 s 15 3580 0 )); /* minmax text */ DATA(insert ( 4056 25 25 1 s 664 3580 0 )); @@ -959,12 +975,16 @@ DATA(insert ( 4056 25 25 2 s 665 3580 0 )); DATA(insert ( 4056 25 25 3 s 98 3580 0 )); DATA(insert ( 4056 25 25 4 s 667 3580 0 )); DATA(insert ( 4056 25 25 5 s 666 3580 0 )); +/* bloom text */ +DATA(insert ( 5027 25 25 1 s 98 3580 0 )); /* minmax oid */ DATA(insert ( 4068 26 26 1 s 609 3580 0 )); DATA(insert ( 4068 26 26 2 s 611 3580 0 )); DATA(insert ( 4068 26 26 3 s 607 3580 0 )); DATA(insert ( 4068 26 26 4 s 612 3580 0 )); DATA(insert ( 4068 26 26 5 s 610 3580 0 )); +/* bloom oid */ +DATA(insert ( 5028 26 26 1 s 607 3580 0 )); /* minmax tid */ DATA(insert ( 4069 27 27 1 s 2799 3580 0 )); DATA(insert ( 4069 27 27 2 s 2801 3580 0 )); @@ -992,6 +1012,11 @@ DATA(insert ( 4070 701 701 2 s 673 3580 0 )); DATA(insert ( 4070 701 701 3 s 670 3580 0 )); DATA(insert ( 4070 701 701 4 s 675 3580 0 )); DATA(insert ( 4070 701 701 5 s 674 3580 0 )); +/* bloom float (float4, float8) */ +DATA(insert ( 5030 700 700 1 s 620 3580 0 )); +DATA(insert ( 5030 700 701 1 s 1120 3580 0 )); +DATA(insert ( 5030 701 700 1 s 1130 3580 0 )); +DATA(insert ( 5030 701 701 1 s 670 3580 0 )); /* minmax abstime */ DATA(insert ( 4072 702 702 1 s 562 3580 0 )); @@ -999,24 +1024,32 @@ DATA(insert ( 4072 702 702 2 s 564 3580 0 )); DATA(insert ( 4072 702 702 3 s 560 3580 0 )); DATA(insert ( 4072 702 702 4 s 565 3580 0 )); DATA(insert ( 4072 702 702 5 s 563 3580 0 )); +/* bloom abstime */ +DATA(insert ( 5031 702 702 1 s 560 3580 0 )); /* minmax reltime */ DATA(insert ( 4073 703 703 1 s 568 3580 0 )); DATA(insert ( 4073 703 703 2 s 570 3580 0 )); DATA(insert ( 4073 703 703 3 s 566 3580 0 )); DATA(insert ( 4073 703 703 4 s 571 3580 0 )); DATA(insert ( 4073 703 703 5 s 569 3580 0 )); +/* bloom reltime */ +DATA(insert ( 5032 703 703 1 s 566 3580 0 )); /* minmax macaddr */ DATA(insert ( 4074 829 829 1 s 1222 3580 0 )); DATA(insert ( 4074 829 829 2 s 1223 3580 0 )); DATA(insert ( 4074 829 829 3 s 1220 3580 0 )); DATA(insert ( 4074 829 829 4 s 1225 3580 0 )); DATA(insert ( 4074 829 829 5 s 1224 3580 0 )); +/* bloom macaddr */ +DATA(insert ( 5033 829 829 1 s 1220 3580 0 )); /* minmax macaddr8 */ DATA(insert ( 4109 774 774 1 s 3364 3580 0 )); DATA(insert ( 4109 774 774 2 s 3365 3580 0 )); DATA(insert ( 4109 774 774 3 s 3362 3580 0 )); DATA(insert ( 4109 774 774 4 s 3367 3580 0 )); DATA(insert ( 4109 774 774 5 s 3366 3580 0 )); +/* bloom macaddr8 */ +DATA(insert ( 5034 774 774 1 s 3362 3580 0 )); /* minmax inet */ DATA(insert ( 4075 869 869 1 s 1203 3580 0 )); DATA(insert ( 4075 869 869 2 s 1204 3580 0 )); @@ -1030,18 +1063,24 @@ DATA(insert ( 4102 869 869 8 s 932 3580 0 )); DATA(insert ( 4102 869 869 18 s 1201 3580 0 )); DATA(insert ( 4102 869 869 24 s 933 3580 0 )); DATA(insert ( 4102 869 869 26 s 931 3580 0 )); +/* bloom inet */ +DATA(insert ( 5035 869 869 1 s 1201 3580 0 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 s 1058 3580 0 )); DATA(insert ( 4076 1042 1042 2 s 1059 3580 0 )); DATA(insert ( 4076 1042 1042 3 s 1054 3580 0 )); DATA(insert ( 4076 1042 1042 4 s 1061 3580 0 )); DATA(insert ( 4076 1042 1042 5 s 1060 3580 0 )); +/* bloom character */ +DATA(insert ( 5036 1042 1042 1 s 1054 3580 0 )); /* minmax time without time zone */ DATA(insert ( 4077 1083 1083 1 s 1110 3580 0 )); DATA(insert ( 4077 1083 1083 2 s 1111 3580 0 )); DATA(insert ( 4077 1083 1083 3 s 1108 3580 0 )); DATA(insert ( 4077 1083 1083 4 s 1113 3580 0 )); DATA(insert ( 4077 1083 1083 5 s 1112 3580 0 )); +/* bloom time without time zone */ +DATA(insert ( 5037 1083 1083 1 s 1108 3580 0 )); /* minmax datetime (date, timestamp, timestamptz) */ DATA(insert ( 4059 1114 1114 1 s 2062 3580 0 )); DATA(insert ( 4059 1114 1114 2 s 2063 3580 0 )); @@ -1088,6 +1127,16 @@ DATA(insert ( 4059 1184 1184 2 s 1323 3580 0 )); DATA(insert ( 4059 1184 1184 3 s 1320 3580 0 )); DATA(insert ( 4059 1184 1184 4 s 1325 3580 0 )); DATA(insert ( 4059 1184 1184 5 s 1324 3580 0 )); +/* bloom datetime (date, timestamp, timestamptz) */ +DATA(insert ( 5038 1114 1114 1 s 2060 3580 0 )); +DATA(insert ( 5038 1114 1082 1 s 2373 3580 0 )); +DATA(insert ( 5038 1114 1184 1 s 2536 3580 0 )); +DATA(insert ( 5038 1082 1082 1 s 1093 3580 0 )); +DATA(insert ( 5038 1082 1114 1 s 2347 3580 0 )); +DATA(insert ( 5038 1082 1184 1 s 2360 3580 0 )); +DATA(insert ( 5038 1184 1082 1 s 2386 3580 0 )); +DATA(insert ( 5038 1184 1114 1 s 2542 3580 0 )); +DATA(insert ( 5038 1184 1184 1 s 1320 3580 0 )); /* minmax interval */ DATA(insert ( 4078 1186 1186 1 s 1332 3580 0 )); @@ -1095,12 +1144,16 @@ DATA(insert ( 4078 1186 1186 2 s 1333 3580 0 )); DATA(insert ( 4078 1186 1186 3 s 1330 3580 0 )); DATA(insert ( 4078 1186 1186 4 s 1335 3580 0 )); DATA(insert ( 4078 1186 1186 5 s 1334 3580 0 )); +/* bloom interval */ +DATA(insert ( 5041 1186 1186 1 s 1330 3580 0 )); /* minmax time with time zone */ DATA(insert ( 4058 1266 1266 1 s 1552 3580 0 )); DATA(insert ( 4058 1266 1266 2 s 1553 3580 0 )); DATA(insert ( 4058 1266 1266 3 s 1550 3580 0 )); DATA(insert ( 4058 1266 1266 4 s 1555 3580 0 )); DATA(insert ( 4058 1266 1266 5 s 1554 3580 0 )); +/* bloom time with time zone */ +DATA(insert ( 5042 1266 1266 1 s 1550 3580 0 )); /* minmax bit */ DATA(insert ( 4079 1560 1560 1 s 1786 3580 0 )); DATA(insert ( 4079 1560 1560 2 s 1788 3580 0 )); @@ -1119,12 +1172,16 @@ DATA(insert ( 4055 1700 1700 2 s 1755 3580 0 )); DATA(insert ( 4055 1700 1700 3 s 1752 3580 0 )); DATA(insert ( 4055 1700 1700 4 s 1757 3580 0 )); DATA(insert ( 4055 1700 1700 5 s 1756 3580 0 )); +/* bloom numeric */ +DATA(insert ( 5045 1700 1700 1 s 1752 3580 0 )); /* minmax uuid */ DATA(insert ( 4081 2950 2950 1 s 2974 3580 0 )); DATA(insert ( 4081 2950 2950 2 s 2976 3580 0 )); DATA(insert ( 4081 2950 2950 3 s 2972 3580 0 )); DATA(insert ( 4081 2950 2950 4 s 2977 3580 0 )); DATA(insert ( 4081 2950 2950 5 s 2975 3580 0 )); +/* bloom uuid */ +DATA(insert ( 5046 2950 2950 1 s 2972 3580 0 )); /* inclusion range types */ DATA(insert ( 4103 3831 3831 1 s 3893 3580 0 )); DATA(insert ( 4103 3831 3831 2 s 3895 3580 0 )); @@ -1146,6 +1203,8 @@ DATA(insert ( 4082 3220 3220 2 s 3226 3580 0 )); DATA(insert ( 4082 3220 3220 3 s 3222 3580 0 )); DATA(insert ( 4082 3220 3220 4 s 3227 3580 0 )); DATA(insert ( 4082 3220 3220 5 s 3225 3580 0 )); +/* bloom pg_lsn */ +DATA(insert ( 5047 3220 3220 1 s 3222 3580 0 )); /* inclusion box */ DATA(insert ( 4104 603 603 1 s 493 3580 0 )); DATA(insert ( 4104 603 603 2 s 494 3580 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 1c95846..cef4a7c 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -341,16 +341,34 @@ DATA(insert ( 4064 17 17 1 3383 )); DATA(insert ( 4064 17 17 2 3384 )); DATA(insert ( 4064 17 17 3 3385 )); DATA(insert ( 4064 17 17 4 3386 )); +/* bloom bytea */ +DATA(insert ( 5021 17 17 1 5017 )); +DATA(insert ( 5021 17 17 2 5018 )); +DATA(insert ( 5021 17 17 3 5019 )); +DATA(insert ( 5021 17 17 4 5020 )); +DATA(insert ( 5021 17 17 11 456 )); /* minmax "char" */ DATA(insert ( 4062 18 18 1 3383 )); DATA(insert ( 4062 18 18 2 3384 )); DATA(insert ( 4062 18 18 3 3385 )); DATA(insert ( 4062 18 18 4 3386 )); +/* bloom "char" */ +DATA(insert ( 5022 18 18 1 5017 )); +DATA(insert ( 5022 18 18 2 5018 )); +DATA(insert ( 5022 18 18 3 5019 )); +DATA(insert ( 5022 18 18 4 5020 )); +DATA(insert ( 5022 18 18 11 454 )); /* minmax name */ DATA(insert ( 4065 19 19 1 3383 )); DATA(insert ( 4065 19 19 2 3384 )); DATA(insert ( 4065 19 19 3 3385 )); DATA(insert ( 4065 19 19 4 3386 )); +/* bloom name */ +DATA(insert ( 5023 19 19 1 5017 )); +DATA(insert ( 5023 19 19 2 5018 )); +DATA(insert ( 5023 19 19 3 5019 )); +DATA(insert ( 5023 19 19 4 5020 )); +DATA(insert ( 5023 19 19 11 455 )); /* minmax integer: int2, int4, int8 */ DATA(insert ( 4054 20 20 1 3383 )); DATA(insert ( 4054 20 20 2 3384 )); @@ -391,16 +409,47 @@ DATA(insert ( 4054 23 21 2 3384 )); DATA(insert ( 4054 23 21 3 3385 )); DATA(insert ( 4054 23 21 4 3386 )); +/* bloom integer: int2, int4, int8 */ +DATA(insert ( 5024 20 20 1 5017 )); +DATA(insert ( 5024 20 20 2 5018 )); +DATA(insert ( 5024 20 20 3 5019 )); +DATA(insert ( 5024 20 20 4 5020 )); +DATA(insert ( 5024 20 20 11 949 )); + +DATA(insert ( 5024 21 21 1 5017 )); +DATA(insert ( 5024 21 21 2 5018 )); +DATA(insert ( 5024 21 21 3 5019 )); +DATA(insert ( 5024 21 21 4 5020 )); +DATA(insert ( 5024 21 21 11 449 )); + +DATA(insert ( 5024 23 23 1 5017 )); +DATA(insert ( 5024 23 23 2 5018 )); +DATA(insert ( 5024 23 23 3 5019 )); +DATA(insert ( 5024 23 23 4 5020 )); +DATA(insert ( 5024 23 23 11 450 )); + /* minmax text */ DATA(insert ( 4056 25 25 1 3383 )); DATA(insert ( 4056 25 25 2 3384 )); DATA(insert ( 4056 25 25 3 3385 )); DATA(insert ( 4056 25 25 4 3386 )); +/* bloom text */ +DATA(insert ( 5027 25 25 1 5017 )); +DATA(insert ( 5027 25 25 2 5018 )); +DATA(insert ( 5027 25 25 3 5019 )); +DATA(insert ( 5027 25 25 4 5020 )); +DATA(insert ( 5027 25 25 11 400 )); /* minmax oid */ DATA(insert ( 4068 26 26 1 3383 )); DATA(insert ( 4068 26 26 2 3384 )); DATA(insert ( 4068 26 26 3 3385 )); DATA(insert ( 4068 26 26 4 3386 )); +/* bloom oid */ +DATA(insert ( 5028 26 26 1 5017 )); +DATA(insert ( 5028 26 26 2 5018 )); +DATA(insert ( 5028 26 26 3 5019 )); +DATA(insert ( 5028 26 26 4 5020 )); +DATA(insert ( 5028 26 26 11 453 )); /* minmax tid */ DATA(insert ( 4069 27 27 1 3383 )); DATA(insert ( 4069 27 27 2 3384 )); @@ -427,26 +476,63 @@ DATA(insert ( 4070 701 700 2 3384 )); DATA(insert ( 4070 701 700 3 3385 )); DATA(insert ( 4070 701 700 4 3386 )); +/* bloom float */ +DATA(insert ( 5030 700 700 1 5017 )); +DATA(insert ( 5030 700 700 2 5018 )); +DATA(insert ( 5030 700 700 3 5019 )); +DATA(insert ( 5030 700 700 4 5020 )); +DATA(insert ( 5030 700 700 11 451 )); + +DATA(insert ( 5030 701 701 1 5017 )); +DATA(insert ( 5030 701 701 2 5018 )); +DATA(insert ( 5030 701 701 3 5019 )); +DATA(insert ( 5030 701 701 4 5020 )); +DATA(insert ( 5030 701 701 11 452 )); + /* minmax abstime */ DATA(insert ( 4072 702 702 1 3383 )); DATA(insert ( 4072 702 702 2 3384 )); DATA(insert ( 4072 702 702 3 3385 )); DATA(insert ( 4072 702 702 4 3386 )); +/* bloom abstime */ +DATA(insert ( 5031 702 702 1 5017 )); +DATA(insert ( 5031 702 702 2 5018 )); +DATA(insert ( 5031 702 702 3 5019 )); +DATA(insert ( 5031 702 702 4 5020 )); +DATA(insert ( 5031 702 702 11 450 )); /* minmax reltime */ DATA(insert ( 4073 703 703 1 3383 )); DATA(insert ( 4073 703 703 2 3384 )); DATA(insert ( 4073 703 703 3 3385 )); DATA(insert ( 4073 703 703 4 3386 )); +/* bloom reltime */ +DATA(insert ( 5032 703 703 1 5017 )); +DATA(insert ( 5032 703 703 2 5018 )); +DATA(insert ( 5032 703 703 3 5019 )); +DATA(insert ( 5032 703 703 4 5020 )); +DATA(insert ( 5032 703 703 11 450 )); /* minmax macaddr */ DATA(insert ( 4074 829 829 1 3383 )); DATA(insert ( 4074 829 829 2 3384 )); DATA(insert ( 4074 829 829 3 3385 )); DATA(insert ( 4074 829 829 4 3386 )); +/* bloom macaddr */ +DATA(insert ( 5033 829 829 1 5017 )); +DATA(insert ( 5033 829 829 2 5018 )); +DATA(insert ( 5033 829 829 3 5019 )); +DATA(insert ( 5033 829 829 4 5020 )); +DATA(insert ( 5033 829 829 11 399 )); /* minmax macaddr8 */ DATA(insert ( 4109 774 774 1 3383 )); DATA(insert ( 4109 774 774 2 3384 )); DATA(insert ( 4109 774 774 3 3385 )); DATA(insert ( 4109 774 774 4 3386 )); +/* bloom macaddr8 */ +DATA(insert ( 5034 774 774 1 5017 )); +DATA(insert ( 5034 774 774 2 5018 )); +DATA(insert ( 5034 774 774 3 5019 )); +DATA(insert ( 5034 774 774 4 5020 )); +DATA(insert ( 5034 774 774 11 328 )); /* minmax inet */ DATA(insert ( 4075 869 869 1 3383 )); DATA(insert ( 4075 869 869 2 3384 )); @@ -460,16 +546,34 @@ DATA(insert ( 4102 869 869 4 4108 )); DATA(insert ( 4102 869 869 11 4063 )); DATA(insert ( 4102 869 869 12 4071 )); DATA(insert ( 4102 869 869 13 930 )); +/* bloom inet */ +DATA(insert ( 5035 869 869 1 5017 )); +DATA(insert ( 5035 869 869 2 5018 )); +DATA(insert ( 5035 869 869 3 5019 )); +DATA(insert ( 5035 869 869 4 5020 )); +DATA(insert ( 5035 869 869 11 422 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 3383 )); DATA(insert ( 4076 1042 1042 2 3384 )); DATA(insert ( 4076 1042 1042 3 3385 )); DATA(insert ( 4076 1042 1042 4 3386 )); +/* bloom character */ +DATA(insert ( 5036 1042 1042 1 5017 )); +DATA(insert ( 5036 1042 1042 2 5018 )); +DATA(insert ( 5036 1042 1042 3 5019 )); +DATA(insert ( 5036 1042 1042 4 5020 )); +DATA(insert ( 5036 1042 1042 11 454 )); /* minmax time without time zone */ DATA(insert ( 4077 1083 1083 1 3383 )); DATA(insert ( 4077 1083 1083 2 3384 )); DATA(insert ( 4077 1083 1083 3 3385 )); DATA(insert ( 4077 1083 1083 4 3386 )); +/* bloom time without time zone */ +DATA(insert ( 5037 1083 1083 1 5017 )); +DATA(insert ( 5037 1083 1083 2 5018 )); +DATA(insert ( 5037 1083 1083 3 5019 )); +DATA(insert ( 5037 1083 1083 4 5020 )); +DATA(insert ( 5037 1083 1083 11 1688 )); /* minmax datetime (date, timestamp, timestamptz) */ DATA(insert ( 4059 1114 1114 1 3383 )); DATA(insert ( 4059 1114 1114 2 3384 )); @@ -510,16 +614,47 @@ DATA(insert ( 4059 1082 1184 2 3384 )); DATA(insert ( 4059 1082 1184 3 3385 )); DATA(insert ( 4059 1082 1184 4 3386 )); +/* bloom datetime (date, timestamp, timestamptz) */ +DATA(insert ( 5038 1114 1114 1 5017 )); +DATA(insert ( 5038 1114 1114 2 5018 )); +DATA(insert ( 5038 1114 1114 3 5019 )); +DATA(insert ( 5038 1114 1114 4 5020 )); +DATA(insert ( 5038 1114 1114 11 2039 )); + +DATA(insert ( 5038 1184 1184 1 5017 )); +DATA(insert ( 5038 1184 1184 2 5018 )); +DATA(insert ( 5038 1184 1184 3 5019 )); +DATA(insert ( 5038 1184 1184 4 5020 )); +DATA(insert ( 5038 1184 1184 11 2039 )); + +DATA(insert ( 5038 1082 1082 1 5017 )); +DATA(insert ( 5038 1082 1082 2 5018 )); +DATA(insert ( 5038 1082 1082 3 5019 )); +DATA(insert ( 5038 1082 1082 4 5020 )); +DATA(insert ( 5038 1082 1082 11 450 )); + /* minmax interval */ DATA(insert ( 4078 1186 1186 1 3383 )); DATA(insert ( 4078 1186 1186 2 3384 )); DATA(insert ( 4078 1186 1186 3 3385 )); DATA(insert ( 4078 1186 1186 4 3386 )); +/* bloom interval */ +DATA(insert ( 5041 1186 1186 1 5017 )); +DATA(insert ( 5041 1186 1186 2 5018 )); +DATA(insert ( 5041 1186 1186 3 5019 )); +DATA(insert ( 5041 1186 1186 4 5020 )); +DATA(insert ( 5041 1186 1186 11 1697 )); /* minmax time with time zone */ DATA(insert ( 4058 1266 1266 1 3383 )); DATA(insert ( 4058 1266 1266 2 3384 )); DATA(insert ( 4058 1266 1266 3 3385 )); DATA(insert ( 4058 1266 1266 4 3386 )); +/* bloom time with time zone */ +DATA(insert ( 5042 1266 1266 1 5017 )); +DATA(insert ( 5042 1266 1266 2 5018 )); +DATA(insert ( 5042 1266 1266 3 5019 )); +DATA(insert ( 5042 1266 1266 4 5020 )); +DATA(insert ( 5042 1266 1266 11 1696 )); /* minmax bit */ DATA(insert ( 4079 1560 1560 1 3383 )); DATA(insert ( 4079 1560 1560 2 3384 )); @@ -535,11 +670,23 @@ DATA(insert ( 4055 1700 1700 1 3383 )); DATA(insert ( 4055 1700 1700 2 3384 )); DATA(insert ( 4055 1700 1700 3 3385 )); DATA(insert ( 4055 1700 1700 4 3386 )); +/* bloom numeric */ +DATA(insert ( 5045 1700 1700 1 5017 )); +DATA(insert ( 5045 1700 1700 2 5018 )); +DATA(insert ( 5045 1700 1700 3 5019 )); +DATA(insert ( 5045 1700 1700 4 5020 )); +DATA(insert ( 5045 1700 1700 11 432 )); /* minmax uuid */ DATA(insert ( 4081 2950 2950 1 3383 )); DATA(insert ( 4081 2950 2950 2 3384 )); DATA(insert ( 4081 2950 2950 3 3385 )); DATA(insert ( 4081 2950 2950 4 3386 )); +/* bloom uuid */ +DATA(insert ( 5046 2950 2950 1 5017 )); +DATA(insert ( 5046 2950 2950 2 5018 )); +DATA(insert ( 5046 2950 2950 3 5019 )); +DATA(insert ( 5046 2950 2950 4 5020 )); +DATA(insert ( 5046 2950 2950 11 2963 )); /* inclusion range types */ DATA(insert ( 4103 3831 3831 1 4105 )); DATA(insert ( 4103 3831 3831 2 4106 )); @@ -553,6 +700,12 @@ DATA(insert ( 4082 3220 3220 1 3383 )); DATA(insert ( 4082 3220 3220 2 3384 )); DATA(insert ( 4082 3220 3220 3 3385 )); DATA(insert ( 4082 3220 3220 4 3386 )); +/* bloom pg_lsn */ +DATA(insert ( 5047 3220 3220 1 5017 )); +DATA(insert ( 5047 3220 3220 2 5018 )); +DATA(insert ( 5047 3220 3220 3 5019 )); +DATA(insert ( 5047 3220 3220 4 5020 )); +DATA(insert ( 5047 3220 3220 11 3252 )); /* inclusion box */ DATA(insert ( 4104 603 603 1 4105 )); DATA(insert ( 4104 603 603 2 4106 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 28dbc74..ce28469 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -213,36 +213,61 @@ DATA(insert ( 2742 jsonb_path_ops PGNSP PGUID 4037 3802 f 23 )); /* BRIN operator classes */ /* no brin opclass for bool */ DATA(insert ( 3580 bytea_minmax_ops PGNSP PGUID 4064 17 t 17 )); +DATA(insert ( 3580 bytea_bloom_ops PGNSP PGUID 5021 17 f 17 )); DATA(insert ( 3580 char_minmax_ops PGNSP PGUID 4062 18 t 18 )); +DATA(insert ( 3580 char_bloom_ops PGNSP PGUID 5022 18 f 18 )); DATA(insert ( 3580 name_minmax_ops PGNSP PGUID 4065 19 t 19 )); +DATA(insert ( 3580 name_bloom_ops PGNSP PGUID 5023 19 f 19 )); DATA(insert ( 3580 int8_minmax_ops PGNSP PGUID 4054 20 t 20 )); +DATA(insert ( 3580 int8_bloom_ops PGNSP PGUID 5024 20 f 20 )); DATA(insert ( 3580 int2_minmax_ops PGNSP PGUID 4054 21 t 21 )); +DATA(insert ( 3580 int2_bloom_ops PGNSP PGUID 5024 21 f 21 )); DATA(insert ( 3580 int4_minmax_ops PGNSP PGUID 4054 23 t 23 )); +DATA(insert ( 3580 int4_bloom_ops PGNSP PGUID 5024 23 f 23 )); DATA(insert ( 3580 text_minmax_ops PGNSP PGUID 4056 25 t 25 )); +DATA(insert ( 3580 text_bloom_ops PGNSP PGUID 5027 25 f 25 )); DATA(insert ( 3580 oid_minmax_ops PGNSP PGUID 4068 26 t 26 )); +DATA(insert ( 3580 oid_bloom_ops PGNSP PGUID 5028 26 f 26 )); DATA(insert ( 3580 tid_minmax_ops PGNSP PGUID 4069 27 t 27 )); DATA(insert ( 3580 float4_minmax_ops PGNSP PGUID 4070 700 t 700 )); +DATA(insert ( 3580 float4_bloom_ops PGNSP PGUID 5030 700 f 700 )); DATA(insert ( 3580 float8_minmax_ops PGNSP PGUID 4070 701 t 701 )); +DATA(insert ( 3580 float8_bloom_ops PGNSP PGUID 5030 701 f 701 )); DATA(insert ( 3580 abstime_minmax_ops PGNSP PGUID 4072 702 t 702 )); +DATA(insert ( 3580 abstime_bloom_ops PGNSP PGUID 5031 702 f 702 )); DATA(insert ( 3580 reltime_minmax_ops PGNSP PGUID 4073 703 t 703 )); +DATA(insert ( 3580 reltime_bloom_ops PGNSP PGUID 5032 703 f 703 )); DATA(insert ( 3580 macaddr_minmax_ops PGNSP PGUID 4074 829 t 829 )); +DATA(insert ( 3580 macaddr_bloom_ops PGNSP PGUID 5033 829 f 829 )); DATA(insert ( 3580 macaddr8_minmax_ops PGNSP PGUID 4109 774 t 774 )); +DATA(insert ( 3580 macaddr8_bloom_ops PGNSP PGUID 5034 774 f 774 )); DATA(insert ( 3580 inet_minmax_ops PGNSP PGUID 4075 869 f 869 )); DATA(insert ( 3580 inet_inclusion_ops PGNSP PGUID 4102 869 t 869 )); +DATA(insert ( 3580 inet_bloom_ops PGNSP PGUID 5035 869 f 869 )); DATA(insert ( 3580 bpchar_minmax_ops PGNSP PGUID 4076 1042 t 1042 )); +DATA(insert ( 3580 bpchar_bloom_ops PGNSP PGUID 5036 1042 f 1042 )); DATA(insert ( 3580 time_minmax_ops PGNSP PGUID 4077 1083 t 1083 )); +DATA(insert ( 3580 time_bloom_ops PGNSP PGUID 5037 1083 f 1083 )); DATA(insert ( 3580 date_minmax_ops PGNSP PGUID 4059 1082 t 1082 )); +DATA(insert ( 3580 date_bloom_ops PGNSP PGUID 5038 1082 f 1082 )); DATA(insert ( 3580 timestamp_minmax_ops PGNSP PGUID 4059 1114 t 1114 )); +DATA(insert ( 3580 timestamp_bloom_ops PGNSP PGUID 5038 1114 f 1114 )); DATA(insert ( 3580 timestamptz_minmax_ops PGNSP PGUID 4059 1184 t 1184 )); +DATA(insert ( 3580 timestamptz_bloom_ops PGNSP PGUID 5038 1184 f 1184 )); DATA(insert ( 3580 interval_minmax_ops PGNSP PGUID 4078 1186 t 1186 )); +DATA(insert ( 3580 interval_bloom_ops PGNSP PGUID 5041 1186 f 1186 )); DATA(insert ( 3580 timetz_minmax_ops PGNSP PGUID 4058 1266 t 1266 )); +DATA(insert ( 3580 timetz_bloom_ops PGNSP PGUID 5042 1266 f 1266 )); DATA(insert ( 3580 bit_minmax_ops PGNSP PGUID 4079 1560 t 1560 )); DATA(insert ( 3580 varbit_minmax_ops PGNSP PGUID 4080 1562 t 1562 )); DATA(insert ( 3580 numeric_minmax_ops PGNSP PGUID 4055 1700 t 1700 )); +DATA(insert ( 3580 numeric_bloom_ops PGNSP PGUID 5045 1700 f 1700 )); /* no brin opclass for record, anyarray */ DATA(insert ( 3580 uuid_minmax_ops PGNSP PGUID 4081 2950 t 2950 )); +DATA(insert ( 3580 uuid_bloom_ops PGNSP PGUID 5046 2950 f 2950 )); DATA(insert ( 3580 range_inclusion_ops PGNSP PGUID 4103 3831 t 3831 )); DATA(insert ( 3580 pg_lsn_minmax_ops PGNSP PGUID 4082 3220 t 3220 )); +DATA(insert ( 3580 pg_lsn_bloom_ops PGNSP PGUID 5047 3220 f 3220 )); /* no brin opclass for enum, tsvector, tsquery, jsonb */ DATA(insert ( 3580 box_inclusion_ops PGNSP PGUID 4104 603 t 603 )); /* no brin opclass for the geometric types except box */ diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index 0d0ba7c..bf9c578 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -160,30 +160,50 @@ DATA(insert OID = 4036 ( 2742 jsonb_ops PGNSP PGUID )); DATA(insert OID = 4037 ( 2742 jsonb_path_ops PGNSP PGUID )); DATA(insert OID = 4054 ( 3580 integer_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5024 ( 3580 integer_bloom_ops PGNSP PGUID )); DATA(insert OID = 4055 ( 3580 numeric_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5045 ( 3580 numeric_bloom_ops PGNSP PGUID )); DATA(insert OID = 4056 ( 3580 text_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5027 ( 3580 text_bloom_ops PGNSP PGUID )); DATA(insert OID = 4058 ( 3580 timetz_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5042 ( 3580 timetz_bloom_ops PGNSP PGUID )); DATA(insert OID = 4059 ( 3580 datetime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5038 ( 3580 datetime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4062 ( 3580 char_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5022 ( 3580 char_bloom_ops PGNSP PGUID )); DATA(insert OID = 4064 ( 3580 bytea_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5021 ( 3580 bytea_bloom_ops PGNSP PGUID )); DATA(insert OID = 4065 ( 3580 name_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5023 ( 3580 name_bloom_ops PGNSP PGUID )); DATA(insert OID = 4068 ( 3580 oid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5028 ( 3580 oid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4069 ( 3580 tid_minmax_ops PGNSP PGUID )); DATA(insert OID = 4070 ( 3580 float_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5030 ( 3580 float_bloom_ops PGNSP PGUID )); DATA(insert OID = 4072 ( 3580 abstime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5031 ( 3580 abstime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4073 ( 3580 reltime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5032 ( 3580 reltime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4074 ( 3580 macaddr_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5033 ( 3580 macaddr_bloom_ops PGNSP PGUID )); DATA(insert OID = 4109 ( 3580 macaddr8_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5034 ( 3580 macaddr8_bloom_ops PGNSP PGUID )); DATA(insert OID = 4075 ( 3580 network_minmax_ops PGNSP PGUID )); DATA(insert OID = 4102 ( 3580 network_inclusion_ops PGNSP PGUID )); +DATA(insert OID = 5035 ( 3580 network_bloom_ops PGNSP PGUID )); DATA(insert OID = 4076 ( 3580 bpchar_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5036 ( 3580 bpchar_bloom_ops PGNSP PGUID )); DATA(insert OID = 4077 ( 3580 time_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5037 ( 3580 time_bloom_ops PGNSP PGUID )); DATA(insert OID = 4078 ( 3580 interval_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5041 ( 3580 interval_bloom_ops PGNSP PGUID )); DATA(insert OID = 4079 ( 3580 bit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4080 ( 3580 varbit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4081 ( 3580 uuid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5046 ( 3580 uuid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4103 ( 3580 range_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5047 ( 3580 pg_lsn_bloom_ops PGNSP PGUID )); DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 93c031a..5852496 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4374,6 +4374,16 @@ DESCR("BRIN inclusion support"); DATA(insert OID = 4108 ( brin_inclusion_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_union _null_ _null_ _null_ )); DESCR("BRIN inclusion support"); +/* BRIN bloom */ +DATA(insert OID = 5017 ( brin_bloom_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_opcinfo _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5018 ( brin_bloom_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_add_value _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5019 ( brin_bloom_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_consistent _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5020 ( brin_bloom_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_union _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); + /* userlock replacements */ DATA(insert OID = 2880 ( pg_advisory_lock PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 2278 "20" _null_ _null_ _null_ _null_ _null_ pg_advisory_lock_int8 _null_ _null_ _null_ )); DESCR("obtain exclusive advisory lock"); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 684f7f2..91b2ecc 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1819,6 +1819,7 @@ ORDER BY 1, 2, 3; 2742 | 11 | ?& 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -1880,7 +1881,7 @@ ORDER BY 1, 2, 3; 4000 | 25 | <<= 4000 | 26 | >> 4000 | 27 | >>= -(121 rows) +(122 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing -- 2.9.5
0002-brin-multi-range-v1.patch (text/x-patch, 38.2K) download From 723d186a99a8eb6f5595713615d4ad85bc4167bc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Tue, 24 Oct 2017 23:35:27 +0200 Subject: [PATCH 2/2] brin multi-range v1 --- src/backend/access/brin/Makefile | 3 +- src/backend/access/brin/brin.c | 36 +- src/backend/access/brin/brin_minmax_multi.c | 888 ++++++++++++++++++++++++++++ src/include/catalog/pg_amop.h | 52 ++ src/include/catalog/pg_amproc.h | 47 ++ src/include/catalog/pg_opclass.h | 4 + src/include/catalog/pg_opfamily.h | 2 + src/include/catalog/pg_proc.h | 10 + 8 files changed, 1039 insertions(+), 3 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index a76d927..c87c796 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,7 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o \ + brin_minmax_multi.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index b3aa6d1..ab8cd05 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -449,6 +449,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) else { int keyno; + bool *attnos; + + attnos = palloc0(sizeof(bool) * bdesc->bd_tupdesc->natts); /* * Compare scan keys with summary values stored for the range. @@ -465,6 +468,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval = &dtup->bt_columns[keyattno - 1]; Datum add; + /* all scan keys for the attribute */ + ScanKey *keys; + int nkeys; + int i; + /* * The collation of the scan key must match the collation * used in the index column (but only if the search is not @@ -487,6 +495,27 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } + /* we process all scankeys on the first lookup */ + if (attnos[keyattno - 1]) + continue; + else + attnos[keyattno - 1] = true; + + /* + * OK, collect all scan keys for this column. + */ + keys = (ScanKey *) palloc0(scan->numberOfKeys * sizeof(ScanKey)); + + nkeys = 0; + for (i = 0; i < scan->numberOfKeys; i++) + { + /* scan is for the *current* attribute, so keep it */ + if (key->sk_attno == keyattno) + keys[nkeys++] = &scan->keyData[i]; + } + + Assert((nkeys > 0) && (nkeys <= scan->numberOfKeys)); + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -497,15 +526,18 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], + add = FunctionCall4Coll(&consistentFn[keyattno - 1], key->sk_collation, PointerGetDatum(bdesc), PointerGetDatum(bval), - PointerGetDatum(key)); + PointerGetDatum(keys), + Int32GetDatum(nkeys)); addrange = DatumGetBool(add); if (!addrange) break; } + + pfree(attnos); } } diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000..94d696e --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,888 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + + +typedef struct MinmaxMultiOpaque +{ + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +#define MINMAX_MAX_VALUES 64 + +typedef struct MinmaxMultiRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* maxvalues >= (2*nranges + nvalues) */ + int maxvalues; /* maximum number of values in the buffer */ + int nranges; /* number of ranges stored in the array */ + int nvalues; /* number of values in the data array */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; + +} MinmaxMultiRanges; + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, + Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the range list, allocate all the memory. + */ +static MinmaxMultiRanges * +minmax_multi_init(int maxvalues) +{ + Size len; + MinmaxMultiRanges *ranges; + + Assert(maxvalues > 0); + Assert(maxvalues <= 1024); /* arbitrary limit */ + + /* + * Allocate the range list with space for the max number of values. + */ + len = offsetof(MinmaxMultiRanges, values) + maxvalues * sizeof(Datum); + + ranges = (MinmaxMultiRanges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + SET_VARSIZE(ranges, len); + + return ranges; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +typedef struct DatumRange { + Datum minval; + Datum maxval; + bool collapsed; +} DatumRange; + +static int +compare_values(const void *a, const void *b, void *arg) +{ + Datum va = *(Datum *)a; + Datum vb = *(Datum *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, va, vb); + + if (DatumGetBool(r)) + return -1; + + r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, vb, va); + + if (DatumGetBool(r)) + return 1; + + return 0; +} + +static int +compare_ranges(const void *a, const void *b, void *arg) +{ + DatumRange ra = *(DatumRange *)a; + DatumRange rb = *(DatumRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + 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)) + return 1; + + return 0; +} + +/* +static void +print_range(char * label, int numvalues, Datum *values) +{ + int idx; + StringInfoData str; + + initStringInfo(&str); + + idx = 0; + while (idx < 2*ranges->nranges) + { + if (idx == 0) + appendStringInfoString(&str, "RANGES: ["); + else + appendStringInfoString(&str, ", "); + + appendStringInfo(&str, "%d => [%.9f, %.9f]", idx/2, DatumGetFloat8(values[idx]), DatumGetFloat8(values[idx+1])); + + idx += 2; + } + + if (ranges->nranges > 0) + appendStringInfoString(&str, "]"); + + if ((ranges->nranges > 0) && (ranges->nvalues > 0)) + appendStringInfoString(&str, " "); + + while (idx < 2*ranges->nranges + ranges->nvalues) + { + if (idx == 2*ranges->nranges) + appendStringInfoString(&str, "VALUES: ["); + else + appendStringInfoString(&str, ", "); + + appendStringInfo(&str, "%.9f", DatumGetFloat8(values[idx])); + + idx++; + } + + if (ranges->nvalues > 0) + appendStringInfoString(&str, "]"); + + elog(WARNING, "%s : %s", label, str.data); + + resetStringInfo(&str); + pfree(str.data); +} +*/ + +/* + * minmax_multi_contains_value + * See if the new value is already contained in the range list. + */ +static bool +minmax_multi_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Oid typid, + MinmaxMultiRanges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* so we're done with the ranges, now let's inspect the exact values */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + + +/* + * minmax_multi_add_value + * See if the new value is already contained in the range list. + */ +static void +minmax_multi_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + MinmaxMultiRanges *ranges, Datum newval) +{ + int i; + + /* context for sorting */ + compare_context cxt; + + Assert(ranges->maxvalues >= 2*ranges->nranges + ranges->nvalues); + + /* + * If there's space in the values array, copy it in and we're done. + * + * If we get duplicates, it doesn't matter as we'll deduplicate the + * values later. + */ + if (ranges->maxvalues > 2*ranges->nranges + ranges->nvalues) + { + ranges->values[2*ranges->nranges + ranges->nvalues] = newval; + ranges->nvalues++; + return; + } + + /* + * There's not enough space, so try deduplicating the values array, + * including the new value. + * + * XXX maybe try deduplicating using memcmp first, instead of using + * the (possibly) fairly complex/expensive comparator. + * + * XXX The if is somewhat unnecessary, because nvalues is always >= 0 + * so we do this always. + */ + if (ranges->nvalues >= 0) + { + FmgrInfo *cmpFn; + int nvalues = ranges->nvalues + 1; /* space for newval */ + Datum *values = palloc(sizeof(Datum) * nvalues); + int idx; + DatumRange *ranges_tmp; + int nranges; + int count; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* copy the existing value and the new value too */ + memcpy(values, &ranges->values[2*ranges->nranges], ranges->nvalues * sizeof(Datum)); + values[ranges->nvalues] = newval; + + /* the actual sort of all the values */ + qsort_arg(values, nvalues, sizeof(Datum), compare_values, (void *) &cxt); + + /* equality for duplicate detection */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + /* keep the first value */ + idx = 1; + for (i = 1; i < nvalues; i++) + { + Datum compar; + + /* is this a new value (different from the previous one)? */ + compar = FunctionCall2Coll(cmpFn, colloid, values[i-1], values[i]); + + /* not equal, we have to store it */ + if (!DatumGetBool(compar)) + values[idx++] = values[i]; + } + + /* + * Have we managed to reduce the number of values? if yes, we can just + * copy it back and we're done. + */ + if (idx < nvalues) + { + memcpy(&ranges->values[2*ranges->nranges], values, idx * sizeof(Datum)); + ranges->nvalues = idx; + pfree(values); + return; + } + + Assert(idx == nvalues); + + /* + * Nope, that didn't work, we have to merge some of the ranges. To do + * that we'll turn the values to "collapsed" ranges (min==max), and + * then merge a bunch of "closest ranges to cut the space requirements + * in half. + * + * XXX Do a merge sort, instead of just using qsort. + */ + nranges = (ranges->nranges + nvalues); + ranges_tmp = palloc0(sizeof(DatumRange) * nranges); + + idx = 0; + + /* ranges */ + for (i = 0; i < ranges->nranges; i++) + { + ranges_tmp[idx].minval = ranges->values[2*i]; + ranges_tmp[idx].maxval = ranges->values[2*i+1]; + ranges_tmp[idx].collapsed = false; + idx++; + } + + /* values as collapsed ranges */ + for (i = 0; i < nvalues; i++) + { + ranges_tmp[idx].minval = values[i]; + ranges_tmp[idx].maxval = values[i]; + ranges_tmp[idx].collapsed = true; + idx++; + } + + Assert(idx == nranges); + + /* sort the ranges */ + qsort_arg(ranges_tmp, nranges, sizeof(DatumRange), compare_ranges, (void *) &cxt); + + /* Now combine as many ranges until the number of values to store + * gets to half of MINMAX_MAX_VALUES. The collapsed ranges will be + * stored as a single value. + */ + count = ranges->nranges * 2 + nvalues; + + while (count > MINMAX_MAX_VALUES/2) + { + int minidx = 0; + double mindistance = DatumGetFloat8(ranges_tmp[1].minval) - DatumGetFloat8(ranges_tmp[0].maxval); + + /* pick the two closest ranges */ + for (i = 1; i < (nranges-1); i++) + { + double distance = DatumGetFloat8(ranges_tmp[i+1].minval) - DatumGetFloat8(ranges_tmp[i-1].maxval); + if (distance < mindistance) + { + mindistance = distance; + minidx = i; + } + } + + /* + * Update the count of Datum values we need to store, depending + * on what type of ranges we merged. + * + * 2 - when both ranges are 'regular' + * 1 - when regular + collapsed + * 0 - when both collapsed + */ + if (!ranges_tmp[minidx].collapsed && !ranges_tmp[minidx+1].collapsed) /* both regular */ + count -= 2; + else if (!ranges_tmp[minidx].collapsed || !ranges_tmp[minidx+1].collapsed) /* one regular */ + count -= 1; + + /* + * combine the two selected ranges, the new range is definiely + * not collapsed + */ + ranges_tmp[minidx].maxval = ranges_tmp[minidx+1].maxval; + ranges_tmp[minidx].collapsed = false; + + for (i = minidx+1; i < nranges-1; i++) + ranges_tmp[i] = ranges_tmp[i+1]; + + nranges--; + + /* + * we can never get zero values + * + * XXX Actually we should never get below (MINMAX_MAX_VALUES/2 - 1) + * values or so. + */ + Assert(count > 0); + } + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < nranges; i++) + { + if (!ranges_tmp[i].collapsed) + { + ranges->values[2*ranges->nranges ] = ranges_tmp[i].minval; + ranges->values[2*ranges->nranges + 1] = ranges_tmp[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < nranges; i++) + { + if (ranges_tmp[i].collapsed) + { + ranges->values[2*ranges->nranges + ranges->nvalues] = ranges_tmp[i].minval; + ranges->nvalues++; + } + } + + pfree(ranges_tmp); + pfree(values); + } +} + + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + + +/* + * Examine the given index tuple (which contains 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_GETARG_DATUM(3); + Oid colloid = PG_GET_COLLATION(); + bool updated = false; + Form_pg_attribute attr; + AttrNumber attno; + MinmaxMultiRanges *ranges; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(MINMAX_MAX_VALUES); + column->bv_values[0] = PointerGetDatum(ranges); + column->bv_allnulls = false; + updated = true; + } + else + ranges = (MinmaxMultiRanges *) DatumGetPointer(column->bv_values[0]); + + /* + * If the new value is already covered by the existing values (or ranges) + * in the BRIN tuple, we're done. We can't really exit when we just + * created the ranges. + */ + if (minmax_multi_contains_value(bdesc, colloid, attno, attr->atttypid, ranges, newval)) + PG_RETURN_BOOL(updated); + + /* */ + minmax_multi_add_value(bdesc, colloid, attno, attr, ranges, newval); + + PG_RETURN_BOOL(true); +} + +/* + * 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; + MinmaxMultiRanges *ranges; + int keyno; + int rangeno; + int i; + + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + } + + /* if the range is all empty, it cannot possibly be consistent */ + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + ranges = (MinmaxMultiRanges *) DatumGetPointer(column->bv_values[0]); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + /* first value from the array */ + matches = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, value, val); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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) +{ + /* FIXME */ + elog(WARNING, "brin_minmax_multi_union not implemented"); + PG_RETURN_VOID(); +} + +/* + * Cache and return the procedure for the given strategy. + * + * Note: this function mirrors inclusion_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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index ef5b692..2052ba7 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -1007,6 +1007,12 @@ DATA(insert ( 4070 701 700 2 s 1134 3580 0 )); DATA(insert ( 4070 701 700 3 s 1130 3580 0 )); DATA(insert ( 4070 701 700 4 s 1135 3580 0 )); DATA(insert ( 4070 701 700 5 s 1133 3580 0 )); +DATA(insert ( 4005 701 701 1 s 672 3580 0 )); +DATA(insert ( 4005 701 701 2 s 673 3580 0 )); +DATA(insert ( 4005 701 701 3 s 670 3580 0 )); +DATA(insert ( 4005 701 701 4 s 675 3580 0 )); +DATA(insert ( 4005 701 701 5 s 674 3580 0 )); +/* minmax multi (float8) */ DATA(insert ( 4070 701 701 1 s 672 3580 0 )); DATA(insert ( 4070 701 701 2 s 673 3580 0 )); DATA(insert ( 4070 701 701 3 s 670 3580 0 )); @@ -1127,6 +1133,52 @@ DATA(insert ( 4059 1184 1184 2 s 1323 3580 0 )); DATA(insert ( 4059 1184 1184 3 s 1320 3580 0 )); DATA(insert ( 4059 1184 1184 4 s 1325 3580 0 )); DATA(insert ( 4059 1184 1184 5 s 1324 3580 0 )); +/* minmax multi (timestamp, timestamptz) */ +DATA(insert ( 4006 1114 1114 1 s 2062 3580 0 )); +DATA(insert ( 4006 1114 1114 2 s 2063 3580 0 )); +DATA(insert ( 4006 1114 1114 3 s 2060 3580 0 )); +DATA(insert ( 4006 1114 1114 4 s 2065 3580 0 )); +DATA(insert ( 4006 1114 1114 5 s 2064 3580 0 )); +DATA(insert ( 4006 1114 1082 1 s 2371 3580 0 )); +DATA(insert ( 4006 1114 1082 2 s 2372 3580 0 )); +DATA(insert ( 4006 1114 1082 3 s 2373 3580 0 )); +DATA(insert ( 4006 1114 1082 4 s 2374 3580 0 )); +DATA(insert ( 4006 1114 1082 5 s 2375 3580 0 )); +DATA(insert ( 4006 1114 1184 1 s 2534 3580 0 )); +DATA(insert ( 4006 1114 1184 2 s 2535 3580 0 )); +DATA(insert ( 4006 1114 1184 3 s 2536 3580 0 )); +DATA(insert ( 4006 1114 1184 4 s 2537 3580 0 )); +DATA(insert ( 4006 1114 1184 5 s 2538 3580 0 )); +DATA(insert ( 4006 1082 1082 1 s 1095 3580 0 )); +DATA(insert ( 4006 1082 1082 2 s 1096 3580 0 )); +DATA(insert ( 4006 1082 1082 3 s 1093 3580 0 )); +DATA(insert ( 4006 1082 1082 4 s 1098 3580 0 )); +DATA(insert ( 4006 1082 1082 5 s 1097 3580 0 )); +DATA(insert ( 4006 1082 1114 1 s 2345 3580 0 )); +DATA(insert ( 4006 1082 1114 2 s 2346 3580 0 )); +DATA(insert ( 4006 1082 1114 3 s 2347 3580 0 )); +DATA(insert ( 4006 1082 1114 4 s 2348 3580 0 )); +DATA(insert ( 4006 1082 1114 5 s 2349 3580 0 )); +DATA(insert ( 4006 1082 1184 1 s 2358 3580 0 )); +DATA(insert ( 4006 1082 1184 2 s 2359 3580 0 )); +DATA(insert ( 4006 1082 1184 3 s 2360 3580 0 )); +DATA(insert ( 4006 1082 1184 4 s 2361 3580 0 )); +DATA(insert ( 4006 1082 1184 5 s 2362 3580 0 )); +DATA(insert ( 4006 1184 1082 1 s 2384 3580 0 )); +DATA(insert ( 4006 1184 1082 2 s 2385 3580 0 )); +DATA(insert ( 4006 1184 1082 3 s 2386 3580 0 )); +DATA(insert ( 4006 1184 1082 4 s 2387 3580 0 )); +DATA(insert ( 4006 1184 1082 5 s 2388 3580 0 )); +DATA(insert ( 4006 1184 1114 1 s 2540 3580 0 )); +DATA(insert ( 4006 1184 1114 2 s 2541 3580 0 )); +DATA(insert ( 4006 1184 1114 3 s 2542 3580 0 )); +DATA(insert ( 4006 1184 1114 4 s 2543 3580 0 )); +DATA(insert ( 4006 1184 1114 5 s 2544 3580 0 )); +DATA(insert ( 4006 1184 1184 1 s 1322 3580 0 )); +DATA(insert ( 4006 1184 1184 2 s 1323 3580 0 )); +DATA(insert ( 4006 1184 1184 3 s 1320 3580 0 )); +DATA(insert ( 4006 1184 1184 4 s 1325 3580 0 )); +DATA(insert ( 4006 1184 1184 5 s 1324 3580 0 )); /* bloom datetime (date, timestamp, timestamptz) */ DATA(insert ( 5038 1114 1114 1 s 2060 3580 0 )); DATA(insert ( 5038 1114 1082 1 s 2373 3580 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index cef4a7c..437d1fb 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -476,6 +476,13 @@ DATA(insert ( 4070 701 700 2 3384 )); DATA(insert ( 4070 701 700 3 3385 )); DATA(insert ( 4070 701 700 4 3386 )); +/* minmax multi float */ + +DATA(insert ( 4005 701 701 1 4001 )); +DATA(insert ( 4005 701 701 2 4002 )); +DATA(insert ( 4005 701 701 3 4003 )); +DATA(insert ( 4005 701 701 4 4004 )); + /* bloom float */ DATA(insert ( 5030 700 700 1 5017 )); DATA(insert ( 5030 700 700 2 5018 )); @@ -614,6 +621,46 @@ DATA(insert ( 4059 1082 1184 2 3384 )); DATA(insert ( 4059 1082 1184 3 3385 )); DATA(insert ( 4059 1082 1184 4 3386 )); +/* minmax multi (timestamp, timestamptz) */ +DATA(insert ( 4006 1114 1114 1 4001 )); +DATA(insert ( 4006 1114 1114 2 4002 )); +DATA(insert ( 4006 1114 1114 3 4003 )); +DATA(insert ( 4006 1114 1114 4 4004 )); +DATA(insert ( 4006 1114 1184 1 4001 )); +DATA(insert ( 4006 1114 1184 2 4002 )); +DATA(insert ( 4006 1114 1184 3 4003 )); +DATA(insert ( 4006 1114 1184 4 4004 )); +DATA(insert ( 4006 1114 1082 1 4001 )); +DATA(insert ( 4006 1114 1082 2 4002 )); +DATA(insert ( 4006 1114 1082 3 4003 )); +DATA(insert ( 4006 1114 1082 4 4004 )); + +DATA(insert ( 4006 1184 1184 1 4001 )); +DATA(insert ( 4006 1184 1184 2 4002 )); +DATA(insert ( 4006 1184 1184 3 4003 )); +DATA(insert ( 4006 1184 1184 4 4004 )); +DATA(insert ( 4006 1184 1114 1 4001 )); +DATA(insert ( 4006 1184 1114 2 4002 )); +DATA(insert ( 4006 1184 1114 3 4003 )); +DATA(insert ( 4006 1184 1114 4 4004 )); +DATA(insert ( 4006 1184 1082 1 4001 )); +DATA(insert ( 4006 1184 1082 2 4002 )); +DATA(insert ( 4006 1184 1082 3 4003 )); +DATA(insert ( 4006 1184 1082 4 4004 )); + +DATA(insert ( 4006 1082 1082 1 4001 )); +DATA(insert ( 4006 1082 1082 2 4002 )); +DATA(insert ( 4006 1082 1082 3 4003 )); +DATA(insert ( 4006 1082 1082 4 4004 )); +DATA(insert ( 4006 1082 1114 1 4001 )); +DATA(insert ( 4006 1082 1114 2 4002 )); +DATA(insert ( 4006 1082 1114 3 4003 )); +DATA(insert ( 4006 1082 1114 4 4004 )); +DATA(insert ( 4006 1082 1184 1 4001 )); +DATA(insert ( 4006 1082 1184 2 4002 )); +DATA(insert ( 4006 1082 1184 3 4003 )); +DATA(insert ( 4006 1082 1184 4 4004 )); + /* bloom datetime (date, timestamp, timestamptz) */ DATA(insert ( 5038 1114 1114 1 5017 )); DATA(insert ( 5038 1114 1114 2 5018 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index ce28469..8f43b66 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -232,6 +232,7 @@ DATA(insert ( 3580 tid_minmax_ops PGNSP PGUID 4069 27 t 27 )); DATA(insert ( 3580 float4_minmax_ops PGNSP PGUID 4070 700 t 700 )); DATA(insert ( 3580 float4_bloom_ops PGNSP PGUID 5030 700 f 700 )); DATA(insert ( 3580 float8_minmax_ops PGNSP PGUID 4070 701 t 701 )); +DATA(insert ( 3580 float8_minmax_multi_ops PGNSP PGUID 4005 701 f 701 )); DATA(insert ( 3580 float8_bloom_ops PGNSP PGUID 5030 701 f 701 )); DATA(insert ( 3580 abstime_minmax_ops PGNSP PGUID 4072 702 t 702 )); DATA(insert ( 3580 abstime_bloom_ops PGNSP PGUID 5031 702 f 702 )); @@ -249,10 +250,13 @@ DATA(insert ( 3580 bpchar_bloom_ops PGNSP PGUID 5036 1042 f 1042 )); DATA(insert ( 3580 time_minmax_ops PGNSP PGUID 4077 1083 t 1083 )); DATA(insert ( 3580 time_bloom_ops PGNSP PGUID 5037 1083 f 1083 )); DATA(insert ( 3580 date_minmax_ops PGNSP PGUID 4059 1082 t 1082 )); +DATA(insert ( 3580 date_minmax_multi_ops PGNSP PGUID 4006 1082 f 1082 )); DATA(insert ( 3580 date_bloom_ops PGNSP PGUID 5038 1082 f 1082 )); DATA(insert ( 3580 timestamp_minmax_ops PGNSP PGUID 4059 1114 t 1114 )); +DATA(insert ( 3580 timestamp_minmax_multi_ops PGNSP PGUID 4006 1114 f 1114 )); DATA(insert ( 3580 timestamp_bloom_ops PGNSP PGUID 5038 1114 f 1114 )); DATA(insert ( 3580 timestamptz_minmax_ops PGNSP PGUID 4059 1184 t 1184 )); +DATA(insert ( 3580 timestamptz_minmax_multi_ops PGNSP PGUID 4006 1184 f 1184 )); DATA(insert ( 3580 timestamptz_bloom_ops PGNSP PGUID 5038 1184 f 1184 )); DATA(insert ( 3580 interval_minmax_ops PGNSP PGUID 4078 1186 t 1186 )); DATA(insert ( 3580 interval_bloom_ops PGNSP PGUID 5041 1186 f 1186 )); diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index bf9c578..90adbbb 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -168,6 +168,7 @@ DATA(insert OID = 5027 ( 3580 text_bloom_ops PGNSP PGUID )); DATA(insert OID = 4058 ( 3580 timetz_minmax_ops PGNSP PGUID )); DATA(insert OID = 5042 ( 3580 timetz_bloom_ops PGNSP PGUID )); DATA(insert OID = 4059 ( 3580 datetime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4006 ( 3580 datetime_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5038 ( 3580 datetime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4062 ( 3580 char_minmax_ops PGNSP PGUID )); DATA(insert OID = 5022 ( 3580 char_bloom_ops PGNSP PGUID )); @@ -179,6 +180,7 @@ DATA(insert OID = 4068 ( 3580 oid_minmax_ops PGNSP PGUID )); DATA(insert OID = 5028 ( 3580 oid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4069 ( 3580 tid_minmax_ops PGNSP PGUID )); DATA(insert OID = 4070 ( 3580 float_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4005 ( 3580 float_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5030 ( 3580 float_bloom_ops PGNSP PGUID )); DATA(insert OID = 4072 ( 3580 abstime_minmax_ops PGNSP PGUID )); DATA(insert OID = 5031 ( 3580 abstime_bloom_ops PGNSP PGUID )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 5852496..87e91de 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4364,6 +4364,16 @@ DESCR("BRIN minmax support"); DATA(insert OID = 3386 ( brin_minmax_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_union _null_ _null_ _null_ )); DESCR("BRIN minmax support"); +/* BRIN minmax multi */ +DATA(insert OID = 4001 ( brin_minmax_multi_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_opcinfo _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4002 ( brin_minmax_multi_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_add_value _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4003 ( brin_minmax_multi_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_consistent _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4004 ( brin_minmax_multi_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_union _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); + /* BRIN inclusion */ DATA(insert OID = 4105 ( brin_inclusion_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_opcinfo _null_ _null_ _null_ )); DESCR("BRIN inclusion support"); -- 2.9.5
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-10-30 20:51   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2017-10-30 20:51 UTC (permalink / raw)
  To: pgsql-hackers

Hi,

attached is a patch series that includes both the BRIN multi-range
minmax indexes discussed in this thread, and the BRIN bloom indexes
initially posted in [1].

It seems easier to just deal with a single patch series, although we may
end up adding just one of those proposed opclasses.

There are 4 parts:

0001 - Modifies bringetbitmap() to pass all scan keys to the consistent
function at once. This is actually needed by the multi-minmax indexes,
but not really required for the others.

I'm wondering if this is a safechange, considering it affects the BRIN
interface. I.e. custom BRIN opclasses (perhaps in extensions) will be
broken by this change. Maybe we should extend the BRIN API to support
two versions of the consistent function - one that processes scan keys
one by one, and the other one that processes all of them at once.

0002 - Adds BRIN bloom indexes, along with opclasses for all built-in
data types (or at least those that also have regular BRIN opclasses).

0003 - Adds BRIN multi-minmax indexes, but only with float8 opclasses
(which also includes timestamp etc.). That should be good enough for
now, but adding support for other data types will require adding some
sort of "distance" operator which is needed for merging ranges (to pick
the two "closest" ones). For float8 it's simply a subtraction.

0004 - Moves dealing with IS [NOT] NULL search keys from opclasses to
bringetbitmap(). The code was exactly the same in all opclasses, so
moving it to bringetbitmap() seems right. It also allows some nice
optimizations where we can skip the consistent() function entirely,
although maybe that's useless. Also, maybe the there are opclasses that
actually need to deal with the NULL values in consistent() function?


regards


[1]
https://www.postgresql.org/message-id/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-BRIN-bloom-indexes.patch.gz (application/gzip, 14.4K) download

  0003-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 12.2K) download

  0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 3.3K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-11-18 20:45     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-30 04:20       ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael.paquier@gmail.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2017-11-18 20:45 UTC (permalink / raw)
  To: pgsql-hackers

Hi,

Apparently there was some minor breakage due to duplicate OIDs, so here
is the patch series updated to current master.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-BRIN-bloom-indexes.patch.gz (application/gzip, 14.4K) download

  0003-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 12.2K) download

  0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 3.3K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-11-30 04:20       ` Michael Paquier <michael.paquier@gmail.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Michael Paquier @ 2017-11-30 04:20 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: pgsql-hackers

On Sun, Nov 19, 2017 at 5:45 AM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> Apparently there was some minor breakage due to duplicate OIDs, so here
> is the patch series updated to current master.

Moved to CF 2018-01.
-- 
Michael




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-12-19 19:38       ` Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Mark Dilger @ 2017-12-19 19:38 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: pgsql-hackers


> On Nov 18, 2017, at 12:45 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
> 
> Hi,
> 
> Apparently there was some minor breakage due to duplicate OIDs, so here
> is the patch series updated to current master.
> 
> regards
> 
> -- 
> Tomas Vondra                  http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
> <0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz><0002-BRIN-bloom-indexes.patch.gz><0003-BRIN-multi-range-minmax-indexes.patch.gz><0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz>


After applying these four patches to my copy of master, the regression
tests fail for F_SATISFIES_HASH_PARTITION 5028 as attached.

mark

Attachments:

  regression.diffs (application/octet-stream, 3.5K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
@ 2017-12-20 01:16         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:07           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2017-12-20 01:16 UTC (permalink / raw)
  To: Mark Dilger <hornschnorter@gmail.com>; +Cc: pgsql-hackers



On 12/19/2017 08:38 PM, Mark Dilger wrote:
> 
>> On Nov 18, 2017, at 12:45 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>
>> Hi,
>>
>> Apparently there was some minor breakage due to duplicate OIDs, so here
>> is the patch series updated to current master.
>>
>> regards
>>
>> -- 
>> Tomas Vondra                  http://www.2ndQuadrant.com
>> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>> <0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz><0002-BRIN-bloom-indexes.patch.gz><0003-BRIN-multi-range-minmax-indexes.patch.gz><0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz>
> 
> 
> After applying these four patches to my copy of master, the regression
> tests fail for F_SATISFIES_HASH_PARTITION 5028 as attached.
> 

D'oh! There was an incorrect OID referenced in pg_opclass, which was
also used by the satisfies_hash_partition() function. Fixed patches
attached.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-BRIN-bloom-indexes.patch.gz (application/gzip, 14.4K) download

  0003-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 12.2K) download

  0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 3.3K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-12-20 02:07           ` Mark Dilger <hornschnorter@gmail.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Mark Dilger @ 2017-12-20 02:07 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: pgsql-hackers


> On Dec 19, 2017, at 5:16 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
> 
> 
> 
> On 12/19/2017 08:38 PM, Mark Dilger wrote:
>> 
>>> On Nov 18, 2017, at 12:45 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>> 
>>> Hi,
>>> 
>>> Apparently there was some minor breakage due to duplicate OIDs, so here
>>> is the patch series updated to current master.
>>> 
>>> regards
>>> 
>>> -- 
>>> Tomas Vondra                  http://www.2ndQuadrant.com
>>> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>>> <0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz><0002-BRIN-bloom-indexes.patch.gz><0003-BRIN-multi-range-minmax-indexes.patch.gz><0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz>
>> 
>> 
>> After applying these four patches to my copy of master, the regression
>> tests fail for F_SATISFIES_HASH_PARTITION 5028 as attached.
>> 
> 
> D'oh! There was an incorrect OID referenced in pg_opclass, which was
> also used by the satisfies_hash_partition() function. Fixed patches
> attached.

Thanks!  These fix the regression test failures.  On my mac, all tests are now
passing.  I have not yet looked any further into the merits of these patches,
however.

mark



^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-12-20 02:37           ` Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Mark Dilger @ 2017-12-20 02:37 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: pgsql-hackers


> On Dec 19, 2017, at 5:16 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
> 
> 
> 
> On 12/19/2017 08:38 PM, Mark Dilger wrote:
>> 
>>> On Nov 18, 2017, at 12:45 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>> 
>>> Hi,
>>> 
>>> Apparently there was some minor breakage due to duplicate OIDs, so here
>>> is the patch series updated to current master.
>>> 
>>> regards
>>> 
>>> -- 
>>> Tomas Vondra                  http://www.2ndQuadrant.com
>>> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>>> <0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz><0002-BRIN-bloom-indexes.patch.gz><0003-BRIN-multi-range-minmax-indexes.patch.gz><0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz>
>> 
>> 
>> After applying these four patches to my copy of master, the regression
>> tests fail for F_SATISFIES_HASH_PARTITION 5028 as attached.
>> 
> 
> D'oh! There was an incorrect OID referenced in pg_opclass, which was
> also used by the satisfies_hash_partition() function. Fixed patches
> attached.

Your use of type ScanKey in src/backend/access/brin/brin.c is a bit confusing.  A
ScanKey is defined elsewhere as a pointer to ScanKeyData.  When you define
an array of ScanKeys, you use pointer-to-pointer style:

+   ScanKey   **keys,
+              **nullkeys;

But when you allocate space for the array, you don't treat it that way:

+   keys = palloc0(sizeof(ScanKey) * bdesc->bd_tupdesc->natts);
+   nullkeys = palloc0(sizeof(ScanKey) * bdesc->bd_tupdesc->natts);

But then again when you use nullkeys, you treat it as a two-dimensional array:

+   nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key;

and likewise when you allocate space within keys:

+    keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);

Could you please clarify?  I have been awake a bit too long; hopefully, I am
not merely missing the obvious.

mark





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
@ 2018-01-07 16:13             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-01-07 16:13 UTC (permalink / raw)
  To: Mark Dilger <hornschnorter@gmail.com>; +Cc: pgsql-hackers



On 12/20/2017 03:37 AM, Mark Dilger wrote:
> 
>> On Dec 19, 2017, at 5:16 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>
>>
>>
>> On 12/19/2017 08:38 PM, Mark Dilger wrote:
>>>
>>>> On Nov 18, 2017, at 12:45 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>>>
>>>> Hi,
>>>>
>>>> Apparently there was some minor breakage due to duplicate OIDs, so here
>>>> is the patch series updated to current master.
>>>>
>>>> regards
>>>>
>>>> -- 
>>>> Tomas Vondra                  http://www.2ndQuadrant.com
>>>> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>>>> <0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz><0002-BRIN-bloom-indexes.patch.gz><0003-BRIN-multi-range-minmax-indexes.patch.gz><0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz>
>>>
>>>
>>> After applying these four patches to my copy of master, the regression
>>> tests fail for F_SATISFIES_HASH_PARTITION 5028 as attached.
>>>
>>
>> D'oh! There was an incorrect OID referenced in pg_opclass, which was
>> also used by the satisfies_hash_partition() function. Fixed patches
>> attached.
> 
> Your use of type ScanKey in src/backend/access/brin/brin.c is a bit confusing.  A
> ScanKey is defined elsewhere as a pointer to ScanKeyData.  When you define
> an array of ScanKeys, you use pointer-to-pointer style:
> 
> +   ScanKey   **keys,
> +              **nullkeys;
> 
> But when you allocate space for the array, you don't treat it that way:
> 
> +   keys = palloc0(sizeof(ScanKey) * bdesc->bd_tupdesc->natts);
> +   nullkeys = palloc0(sizeof(ScanKey) * bdesc->bd_tupdesc->natts);
> 
> But then again when you use nullkeys, you treat it as a two-dimensional array:
> 
> +   nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key;
> 
> and likewise when you allocate space within keys:
> 
> +    keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
> 
> Could you please clarify?  I have been awake a bit too long; hopefully, I am
> not merely missing the obvious.
> 

Yeah, that's wrong - it should be "sizeof(ScanKey *)" instead. It's
harmless, though, because ScanKey itself is a pointer, so the size is
the same.

Attached is an updated version of the patch series, significantly
reworking and improving the multi-minmax part (the rest of the patch is
mostly as it was before).

I've significantly refactored and cleaned up the multi-minmax part, and
I'm much happier with it - no doubt there's room for further improvement
but overall it's much better.

I've also added proper sgml docs for this part, and support for more
data types including variable-length ones (all integer types, numeric,
float-based types including timestamps, uuid, and a couple of others).

At the API level, I needed to add one extra support procedure that
measures distance between two values of the data type. This is needed so
because we only keep a limited number of intervals for each range, and
once in a while we need to decide which of them to "merge" (and we
simply merge the closest ones).

I've passed the indexes through significant testing and fixed a couple
of silly bugs / memory leaks. Let's see if there are more.

Performance-wise, the CREATE INDEX seems a bit slow - it's about an
order of magnitude slower than regular BRIN. Some of that is expected
(we simply need to do more stuff to maintain multiple ranges), but
perhaps there's room for additional improvements - that's something I'd
like to work on next.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-BRIN-bloom-indexes.patch.gz (application/gzip, 14.4K) download

  0003-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 20.9K) download

  0004-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 3.3K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-01-23 20:05               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2018-01-23 20:05 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

This stuff sounds pretty nice.  However, have a look at this report:

https://codecov.io/gh/postgresql-cfbot/postgresql/commit/2aa632dae3066900e15d2d42a4aad811dec11f08

it seems to me that the new code is not tested at all.  Shouldn't you
add a few more tests?

I think 0004 should apply to unpatched master (except for the parts that
concern files not in master); sounds like a good candidate for first
apply.  Then 0001, which seems mostly just refactoring.  0002 and 0003
are the really interesting ones (minus the code removed by 0004).

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-01-23 21:07                 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-01-23 21:07 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers



On 01/23/2018 09:05 PM, Alvaro Herrera wrote:
> This stuff sounds pretty nice.  However, have a look at this report:
> 
> https://codecov.io/gh/postgresql-cfbot/postgresql/commit/2aa632dae3066900e15d2d42a4aad811dec11f08
> 
> it seems to me that the new code is not tested at all.  Shouldn't you
> add a few more tests?
> 

I have a hard time reading the report, but you're right I haven't added
any tests for the new opclasses (bloom and minmax_multi). I agree that's
something that needs to be addressed.

> I think 0004 should apply to unpatched master (except for the parts
> that concern files not in master); sounds like a good candidate for
> first apply. Then 0001, which seems mostly just refactoring. 0002 and
> 0003 are the really interesting ones (minus the code removed by
> 0004).
> 

That sounds like a reasonable plan. I'll reorder the patch series along
those lines in the next few days.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-02-05 18:57                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-02-05 18:57 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers



On 01/23/2018 10:07 PM, Tomas Vondra wrote:
> 
> 
> On 01/23/2018 09:05 PM, Alvaro Herrera wrote:
>> This stuff sounds pretty nice.  However, have a look at this report:
>>
>> https://codecov.io/gh/postgresql-cfbot/postgresql/commit/2aa632dae3066900e15d2d42a4aad811dec11f08
>>
>> it seems to me that the new code is not tested at all.  Shouldn't you
>> add a few more tests?
>>
> 
> I have a hard time reading the report, but you're right I haven't added
> any tests for the new opclasses (bloom and minmax_multi). I agree that's
> something that needs to be addressed.
> 
>> I think 0004 should apply to unpatched master (except for the parts
>> that concern files not in master); sounds like a good candidate for
>> first apply. Then 0001, which seems mostly just refactoring. 0002 and
>> 0003 are the really interesting ones (minus the code removed by
>> 0004).
>>
> 
> That sounds like a reasonable plan. I'll reorder the patch series along
> those lines in the next few days.
> 

And here we go. Attached is a reworked patch series that moves the IS
NULL tweak to the beginning of the series, and also adds proper
regression tests both for the bloom and multi-minmax opclasses. I've
simply copied the brin.sql tests and tweaked it for the new opclasses.

I've also added a bunch of missing multi-minmax opclasses. At this point
all data types that have minmax opclass should also have multi-minmax
one, except for these types:

* bytea
* char
* name
* text
* bpchar
* bit
* varbit

The reason is that I'm not quite sure how to define the 'distance'
function, which is needed when picking ranges to merge when
building/updating the index.

BTW while working on the regression tests, I've noticed that brin.sql
fails to test a couple of minmax opclasses (e.g. abstime/reltime). Is
that intentional or is that something we should fix eventually?

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (4 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch (text/x-patch, 29.3K) download From bbaaf2dd5ac6fcfff37b226afa1197b02c48ce8b Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Tue, 9 Jan 2018 00:59:09 +0100 Subject: [PATCH 1/4] Pass all keys to BRIN consistent function at once When building the bitmap, bringetbitmap was passing the scan keys to consistent support procedure one by one. That works fine for the simple default opclasses (minmax and inclusion), but not for opclasses with more complicated summaries. For example the multi-minmax opclass needs to see all the keys to efficiently eliminate the partial ranges, otherwise it's mostly just a more expensive minmax opclass. So instead split the scan keys into per-attribute groups, and pass the whole group at once. --- src/backend/access/brin/brin.c | 97 +++++-- src/backend/access/brin/brin_inclusion.c | 484 ++++++++++++++++++------------- src/backend/access/brin/brin_minmax.c | 168 +++++++---- 3 files changed, 451 insertions(+), 298 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 68b3371..b568786 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -367,6 +367,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -388,6 +391,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -448,7 +498,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -458,34 +508,26 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; + Oid collation; + + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; + + bval = &dtup->bt_columns[attno - 1]; + + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... + * Collation from the first key (has to be the same for + * all keys for the same attribue). */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); - - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; - - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + collation = keys[attno - 1][0]->sk_collation; /* * Check whether the scan key is consistent with the page @@ -497,11 +539,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, PointerGetDatum(bdesc), PointerGetDatum(bval), - PointerGetDatum(key)); + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); addrange = DatumGetBool(add); if (!addrange) break; diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 2542877..fb7f2b0 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -248,7 +248,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(), subtype; Datum unionval; @@ -256,235 +257,300 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Datum query; FmgrInfo *finfo; Datum result; + int keyno; + bool matches; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + ScanKey key = keys[keyno]; - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } + Assert(key->sk_attno == column->bv_attno); - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* It has to be checked, if it contains elements that are not mergeable. */ - if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(false); + } - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; - switch (key->sk_strategy) - { /* - * Placement strategies - * - * These are implemented by logically negating the result of the - * converse placement operator; for this to work, the converse - * operator must be part of the opclass. An error will be thrown - * by inclusion_get_strategy_procinfo() if the required strategy - * is not part of the opclass. - * - * These all return false if either argument is empty, so there is - * no need to check for empty elements. + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); - case RTLeftStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverRightStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTOverLeftStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTRightStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTOverRightStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTLeftStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTRightStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverLeftStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTBelowStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverAboveStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTOverBelowStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAboveStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTOverAboveStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTBelowStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - case RTAboveStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverBelowStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + continue; + } /* - * Overlap and contains strategies - * - * These strategies are simple enough that we can simply call the - * operator and return its result. Empty elements don't change - * the result. + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - case RTOverlapStrategyNumber: - case RTContainsStrategyNumber: - case RTOldContainsStrategyNumber: - case RTContainsElemStrategyNumber: - case RTSubStrategyNumber: - case RTSubEqualStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - key->sk_strategy); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); - - /* - * Contained by strategies - * - * We cannot just call the original operator for the contained by - * strategies because some elements can be contained even though - * the union is not; instead we use the overlap operator. - * - * We check for empty elements separately as they are not merged - * to the union but contained by everything. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); - case RTContainedByStrategyNumber: - case RTOldContainedByStrategyNumber: - case RTSuperStrategyNumber: - case RTSuperEqualStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverlapStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + /* It has to be checked, if it contains elements that are not mergeable. */ + if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) + PG_RETURN_BOOL(true); - /* - * Adjacent strategy - * - * We test for overlap first but to be safe we need to call the - * actual adjacent operator also. - * - * An empty element cannot be adjacent to any other, so there is - * no need to check for it. - */ + matches = true; - case RTAdjacentStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTOverlapStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; - /* - * Basic comparison strategies - * - * It is straightforward to support the equality strategies with - * the contains operator. Generally, inequality strategies do not - * make much sense for the types which will be used with the - * inclusion BRIN family of opclasses, but is possible to - * implement them with logical negation of the left-of and - * right-of operators. - * - * NB: These strategies cannot be used with geometric datatypes - * that use comparison of areas! The only exception is the "same" - * strategy. - * - * Empty elements are considered to be less than the others. We - * cannot use the empty support function to check the query is an - * empty element, because the query can be another data type than - * the empty support function argument. So we will return true, - * if there is a possibility that empty elements will change the - * result. - */ + attno = key->sk_attno; + subtype = key->sk_subtype; + query = key->sk_argument; + unionval = column->bv_values[INCLUSION_UNION]; + switch (key->sk_strategy) + { + /* + * Placement strategies + * + * These are implemented by logically negating the result of the + * converse placement operator; for this to work, the converse + * operator must be part of the opclass. An error will be thrown + * by inclusion_get_strategy_procinfo() if the required strategy + * is not part of the opclass. + * + * These all return false if either argument is empty, so there is + * no need to check for empty elements. + */ + + case RTLeftStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTOverLeftStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTOverRightStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTRightStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTBelowStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverAboveStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTOverBelowStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAboveStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTOverAboveStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTBelowStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + case RTAboveStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverBelowStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + /* + * Overlap and contains strategies + * + * These strategies are simple enough that we can simply call the + * operator and return its result. Empty elements don't change + * the result. + */ + + case RTOverlapStrategyNumber: + case RTContainsStrategyNumber: + case RTOldContainsStrategyNumber: + case RTContainsElemStrategyNumber: + case RTSubStrategyNumber: + case RTSubEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = DatumGetBool(result); + break; + + /* + * Contained by strategies + * + * We cannot just call the original operator for the contained by + * strategies because some elements can be contained even though + * the union is not; instead we use the overlap operator. + * + * We check for empty elements separately as they are not merged + * to the union but contained by everything. + */ + + case RTContainedByStrategyNumber: + case RTOldContainedByStrategyNumber: + case RTSuperStrategyNumber: + case RTSuperEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + matches = true; + else + matches = DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + break; + + /* + * Adjacent strategy + * + * We test for overlap first but to be safe we need to call the + * actual adjacent operator also. + * + * An empty element cannot be adjacent to any other, so there is + * no need to check for it. + */ + + case RTAdjacentStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + matches = (true); + else + { + + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAdjacentStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = DatumGetBool(result); + } + + break; + + /* + * Basic comparison strategies + * + * It is straightforward to support the equality strategies with + * the contains operator. Generally, inequality strategies do not + * make much sense for the types which will be used with the + * inclusion BRIN family of opclasses, but is possible to + * implement them with logical negation of the left-of and + * right-of operators. + * + * NB: These strategies cannot be used with geometric datatypes + * that use comparison of areas! The only exception is the "same" + * strategy. + * + * Empty elements are considered to be less than the others. We + * cannot use the empty support function to check the query is an + * empty element, because the query can be another data type than + * the empty support function argument. So we will return true, + * if there is a possibility that empty elements will change the + * result. + */ + + case RTLessStrategyNumber: + case RTLessEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (!DatumGetBool(result)) + matches = (true); + else + matches = DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + break; + + case RTSameStrategyNumber: + case RTEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTContainsStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + matches = (true); + else + matches = DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + break; + + case RTGreaterEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (!DatumGetBool(result)) + matches = (true); + else + matches = DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + break; + + case RTGreaterStrategyNumber: + /* no need to check for empty elements */ + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + matches = (!DatumGetBool(result)); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = (false); + } - case RTLessStrategyNumber: - case RTLessEqualStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTRightStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); - - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); - - case RTSameStrategyNumber: - case RTEqualStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTContainsStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - if (DatumGetBool(result)) - PG_RETURN_BOOL(true); - - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); - - case RTGreaterEqualStrategyNumber: - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTLeftStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); - - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); - - case RTGreaterStrategyNumber: - /* no need to check for empty elements */ - finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTLeftStrategyNumber); - result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); - - default: - /* shouldn't happen */ - elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + if (!matches) + break; } + + PG_RETURN_BOOL(matches); } /* diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 0f6aa33..e58fd16 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -147,86 +147,130 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(), subtype; AttrNumber attno; Datum value; Datum matches; FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; - switch (key->sk_strategy) + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) { - case BTLessStrategyNumber: - case BTLessEqualStrategyNumber: - finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, - key->sk_strategy); - matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0], - value); - break; - case BTEqualStrategyNumber: + ScanKey key = keys[keyno]; - /* - * In the equality case (WHERE col = someval), we want to return - * the current page range if the minimum value in the range <= - * scan key, and the maximum value >= scan key. - */ - finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, - BTLessEqualStrategyNumber); - matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0], - value); - if (!DatumGetBool(matches)) + /* ignore IS NULL/IS NOT NULL tests handled above */ + 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: + finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0], + value); break; - /* max() >= scankey */ - finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, - BTGreaterEqualStrategyNumber); - matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1], - value); - break; - case BTGreaterEqualStrategyNumber: - case BTGreaterStrategyNumber: - finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, - key->sk_strategy); - matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1], - value); - break; - default: - /* shouldn't happen */ - elog(ERROR, "invalid strategy number %d", key->sk_strategy); - matches = 0; + case BTEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, + BTLessEqualStrategyNumber); + matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0], + value); + if (!DatumGetBool(matches)) + break; + /* max() >= scankey */ + finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterEqualStrategyNumber); + matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1], + value); + break; + case BTGreaterEqualStrategyNumber: + case BTGreaterStrategyNumber: + finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1], + value); + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* found non-matching key */ + if (!matches) break; } -- 2.9.5
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch (text/x-patch, 10.3K) download From ed8b14643a8c313767f8285b40b44f96a9b8ece3 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 4 Feb 2018 14:27:42 +0100 Subject: [PATCH 2/4] Move IS [NOT] NULL checks to bringetbitmap These checks are exactly the same in all BRIN opclasses, so it makes sense to move them to bringetbimap() and evaluate them there. It also allows us to handle some ranges without invoking the consistent support procedure (e.g. when the key says IS NULL) but the range is marked as not containing any NULL values. Note: It may also be a good idea to first process NULL keys for all attributes on a given page range, and then only proceed with the remaining keys if it's still needed (when the page range is not eliminated by NULL keys on any attribute). --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +---------------- src/backend/access/brin/brin_minmax.c | 62 +---------------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index b568786..7786f58 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -367,8 +367,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -393,10 +395,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -418,14 +423,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -433,9 +436,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -530,6 +547,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) collation = keys[attno - 1][0]->sk_collation; /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index fb7f2b0..6d51c71 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -259,63 +259,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Datum result; int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -327,9 +270,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); attno = key->sk_attno; subtype = key->sk_subtype; diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index e58fd16..4621bea 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -156,63 +156,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Datum matches; FmgrInfo *finfo; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -220,9 +163,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); attno = key->sk_attno; subtype = key->sk_subtype; -- 2.9.5
0003-BRIN-bloom-indexes.patch (text/x-patch, 89.8K) download From a67c77d7754de938f90fbd60044c5c0fcccc6395 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Tue, 9 Jan 2018 01:00:39 +0100 Subject: [PATCH 3/4] BRIN bloom indexes An opclass implementing BRIN indexes with bloom filter summary. Naturally, this opclass only supports equality searches, but that is fine for many use cases (e.g. it's uncommon to do range queries on identifiers or labels). It's not quite clear how to size the bloom filter, or what to do if it gets too full (more than 50% of bits set). The patch picks some reasonable defaults, but perhaps we can improve this based on more information (ndistinct from pg_stats, pages_per_range, ...). Or make it configurable when creating the index. Another option would be to implement scalable bloom filters, i.e. instead of creating one filter and stop using it when it gets too full, create another (larger) one and use it for new additions. But considering we can only see very limited number of distinct values per range, that seems like an overkill, and it might make certain operations (particularly "union" support procedure) more complicated. The patch also implements an optimization that we initially keep the values "raw", and only switch to the actual bloom filter once we use the same space. This somewhat reduces the space usage when the column is low-cardinality (at least within a page range), so that the bloom filter defaults would be too high. --- doc/src/sgml/brin.sgml | 229 ++++++++++ src/backend/access/brin/Makefile | 2 +- src/backend/access/brin/brin_bloom.c | 755 +++++++++++++++++++++++++++++++ src/include/catalog/pg_amop.h | 59 +++ src/include/catalog/pg_amproc.h | 153 +++++++ src/include/catalog/pg_opclass.h | 25 + src/include/catalog/pg_opfamily.h | 20 + src/include/catalog/pg_proc.h | 10 + src/test/regress/expected/brin_bloom.out | 438 ++++++++++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 391 ++++++++++++++++ 13 files changed, 2085 insertions(+), 3 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 23c0e05..434538e 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -118,6 +118,13 @@ </thead> <tbody> <row> + <entry><literal>abstime_bloom_ops</literal></entry> + <entry><type>abstime</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>abstime_minmax_ops</literal></entry> <entry><type>abstime</type></entry> <entry> @@ -129,6 +136,13 @@ </entry> </row> <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> <entry> @@ -180,6 +194,13 @@ </entry> </row> <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> <entry> @@ -191,6 +212,13 @@ </entry> </row> <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> <entry> @@ -202,6 +230,13 @@ </entry> </row> <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> <entry> @@ -213,6 +248,13 @@ </entry> </row> <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> <entry> @@ -224,6 +266,13 @@ </entry> </row> <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> <entry> @@ -235,6 +284,13 @@ </entry> </row> <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> <entry> @@ -258,6 +314,13 @@ </entry> </row> <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> <entry> @@ -269,6 +332,13 @@ </entry> </row> <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> <entry> @@ -280,6 +350,13 @@ </entry> </row> <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> <entry> @@ -291,6 +368,13 @@ </entry> </row> <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> <entry> @@ -302,6 +386,13 @@ </entry> </row> <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> <entry> @@ -313,6 +404,13 @@ </entry> </row> <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> <entry> @@ -324,6 +422,13 @@ </entry> </row> <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> <entry> @@ -335,6 +440,13 @@ </entry> </row> <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> <entry> @@ -366,6 +478,13 @@ </entry> </row> <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> <entry> @@ -377,6 +496,13 @@ </entry> </row> <row> + <entry><literal>reltime_bloom_ops</literal></entry> + <entry><type>reltime</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>reltime_minmax_ops</literal></entry> <entry><type>reltime</type></entry> <entry> @@ -388,6 +514,13 @@ </entry> </row> <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> <entry> @@ -399,6 +532,13 @@ </entry> </row> <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> <entry> @@ -421,6 +561,13 @@ </entry> </row> <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> <entry> @@ -432,6 +579,13 @@ </entry> </row> <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> <entry> @@ -443,6 +597,13 @@ </entry> </row> <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> <entry> @@ -454,6 +615,13 @@ </entry> </row> <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> <entry> @@ -465,6 +633,13 @@ </entry> </row> <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> <entry> @@ -814,6 +989,60 @@ typedef struct BrinOpcInfo </para> <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. The minmax operator class requires a full set of operators to be diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 5aef925..a76d927 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,6 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000..461bfce --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,755 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 4 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +#define BLOOM_PHASE_SORTED 1 +#define BLOOM_PHASE_HASH 2 + +/* how many hashes to accumulate before hashing */ +#define BLOOM_MAX_UNSORTED 32 +#define BLOOM_GROW_BYTES 32 +#define BLOOM_NDISTINCT 1000 /* number of distinct values */ +#define BLOOM_ERROR_RATE 0.05 /* 2% false positive rate */ + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Maybe instead of explicitly limiting the number of unsorted values + * by BLOOM_MAX_UNSORTED, we should cap them by (filter size / 4B), i.e. + * allow up to the whole filter size. + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* global bloom filter parameters */ + uint32 phase; /* phase (initially SORTED, then HASH) */ + uint32 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (optimal) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* fields used only in the EXACT phase */ + uint32 nvalues; /* number of hashes stored (sorted + extra) */ + uint32 nsorted; /* number of uint32 hashes in sorted part */ + + /* bitmap of the bloom filter */ + char bitmap[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + /* https://en.wikipedia.org/wiki/Bloom_filter */ + int m; /* number of bits */ + int k; /* number of hash functions */ + + Assert((ndistinct > 0) && (ndistinct < 10000)); /* 10k is mostly arbitrary limit */ + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + k = round(log(2.0) * m / ndistinct); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + */ + len = Max(offsetof(BloomFilter, bitmap), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->phase = BLOOM_PHASE_SORTED; + filter->nhashes = k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * XXX Actually, we don't need to do repalloc - we just need to set the + * varlena header length! + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(filter->phase == BLOOM_PHASE_SORTED); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) palloc(filter->nvalues * sizeof(uint32)); + + /* copy the data, then reset the bitmap */ + memcpy(values, filter->bitmap, filter->nvalues * sizeof(uint32)); + memset(filter->bitmap, 0, filter->nvalues * sizeof(uint32)); + + /* FIXME optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if a new value, keep it */ + if (values[i] != values[i-1]) + { + values[nvalues] = values[i]; + nvalues++; + } + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + memcpy(filter->bitmap, values, nvalues * sizeof(uint32)); + + pfree(values); +} + +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter); + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter,bitmap) + (filter->nvalues+1) * sizeof(uint32); + + if (len < need) + { + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts anyway. + * + * XXX Zero the newly allocated part. Maybe not really needed? + */ + filter = (BloomFilter *) repalloc(filter, len + BLOOM_GROW_BYTES); + memset((char *)filter + len, 0, BLOOM_GROW_BYTES); + SET_VARSIZE(filter, len + BLOOM_GROW_BYTES); + } + + /* copy the data into the bitmap */ + memcpy(&filter->bitmap[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32)DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + { + filter->bitmap[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) h -= filter->nbits; + if (d == filter->nbits) d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, bitmap) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->phase = BLOOM_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->bitmap; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + int i; + uint32 *values = (uint32 *)filter->bitmap; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + if (value == values[i]) + return true; + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + big_h = ((uint32)DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) h -= filter->nbits; + if (d == filter->nbits) d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store a the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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_GETARG_DATUM(3); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(BLOOM_NDISTINCT, BLOOM_ERROR_RATE); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + + /* Adjust "hasnulls" */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do */ + if (col_b->bv_allnulls) + PG_RETURN_VOID(); + + /* + * Adjust "allnulls". If A doesn't have values, just copy the values from + * B into A, and we're done. We cannot run the operators in this case, + * because values in A might contain garbage. Note we already established + * that B contains values. + */ + if (col_a->bv_allnulls) + { + col_a->bv_allnulls = false; + col_a->bv_values[0] = datumCopy(col_b->bv_values[0], false, -1); + PG_RETURN_VOID(); + } + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (filter_b->phase == BLOOM_PHASE_SORTED) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->bitmap; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (filter_a->phase == BLOOM_PHASE_SORTED) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->bitmap; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (filter_a->phase == BLOOM_PHASE_HASH) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + for (i = 0; i < nbytes; i++) + filter_a->bitmap[i] |= filter_b->bitmap[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 03af581..84dc6e3 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -910,18 +910,24 @@ DATA(insert ( 4064 17 17 2 s 1958 3580 0 )); DATA(insert ( 4064 17 17 3 s 1955 3580 0 )); DATA(insert ( 4064 17 17 4 s 1960 3580 0 )); DATA(insert ( 4064 17 17 5 s 1959 3580 0 )); +/* bloom bytea */ +DATA(insert ( 5021 17 17 1 s 1955 3580 0 )); /* minmax "char" */ DATA(insert ( 4062 18 18 1 s 631 3580 0 )); DATA(insert ( 4062 18 18 2 s 632 3580 0 )); DATA(insert ( 4062 18 18 3 s 92 3580 0 )); DATA(insert ( 4062 18 18 4 s 634 3580 0 )); DATA(insert ( 4062 18 18 5 s 633 3580 0 )); +/* bloom "char" */ +DATA(insert ( 5022 18 18 1 s 92 3580 0 )); /* minmax name */ DATA(insert ( 4065 19 19 1 s 660 3580 0 )); DATA(insert ( 4065 19 19 2 s 661 3580 0 )); DATA(insert ( 4065 19 19 3 s 93 3580 0 )); DATA(insert ( 4065 19 19 4 s 663 3580 0 )); DATA(insert ( 4065 19 19 5 s 662 3580 0 )); +/* bloom name */ +DATA(insert ( 5023 19 19 1 s 93 3580 0 )); /* minmax integer */ DATA(insert ( 4054 20 20 1 s 412 3580 0 )); DATA(insert ( 4054 20 20 2 s 414 3580 0 )); @@ -968,6 +974,16 @@ DATA(insert ( 4054 23 20 2 s 80 3580 0 )); DATA(insert ( 4054 23 20 3 s 15 3580 0 )); DATA(insert ( 4054 23 20 4 s 82 3580 0 )); DATA(insert ( 4054 23 20 5 s 76 3580 0 )); +/* bloom integer */ +DATA(insert ( 5024 20 20 1 s 410 3580 0 )); +DATA(insert ( 5024 20 21 1 s 1868 3580 0 )); +DATA(insert ( 5024 20 23 1 s 416 3580 0 )); +DATA(insert ( 5024 21 21 1 s 94 3580 0 )); +DATA(insert ( 5024 21 20 1 s 1862 3580 0 )); +DATA(insert ( 5024 21 23 1 s 532 3580 0 )); +DATA(insert ( 5024 23 23 1 s 96 3580 0 )); +DATA(insert ( 5024 23 21 1 s 533 3580 0 )); +DATA(insert ( 5024 23 20 1 s 15 3580 0 )); /* minmax text */ DATA(insert ( 4056 25 25 1 s 664 3580 0 )); @@ -975,12 +991,16 @@ DATA(insert ( 4056 25 25 2 s 665 3580 0 )); DATA(insert ( 4056 25 25 3 s 98 3580 0 )); DATA(insert ( 4056 25 25 4 s 667 3580 0 )); DATA(insert ( 4056 25 25 5 s 666 3580 0 )); +/* bloom text */ +DATA(insert ( 5027 25 25 1 s 98 3580 0 )); /* minmax oid */ DATA(insert ( 4068 26 26 1 s 609 3580 0 )); DATA(insert ( 4068 26 26 2 s 611 3580 0 )); DATA(insert ( 4068 26 26 3 s 607 3580 0 )); DATA(insert ( 4068 26 26 4 s 612 3580 0 )); DATA(insert ( 4068 26 26 5 s 610 3580 0 )); +/* bloom oid */ +DATA(insert ( 5029 26 26 1 s 607 3580 0 )); /* minmax tid */ DATA(insert ( 4069 27 27 1 s 2799 3580 0 )); DATA(insert ( 4069 27 27 2 s 2801 3580 0 )); @@ -1008,6 +1028,11 @@ DATA(insert ( 4070 701 701 2 s 673 3580 0 )); DATA(insert ( 4070 701 701 3 s 670 3580 0 )); DATA(insert ( 4070 701 701 4 s 675 3580 0 )); DATA(insert ( 4070 701 701 5 s 674 3580 0 )); +/* bloom float (float4, float8) */ +DATA(insert ( 5030 700 700 1 s 620 3580 0 )); +DATA(insert ( 5030 700 701 1 s 1120 3580 0 )); +DATA(insert ( 5030 701 700 1 s 1130 3580 0 )); +DATA(insert ( 5030 701 701 1 s 670 3580 0 )); /* minmax abstime */ DATA(insert ( 4072 702 702 1 s 562 3580 0 )); @@ -1015,24 +1040,32 @@ DATA(insert ( 4072 702 702 2 s 564 3580 0 )); DATA(insert ( 4072 702 702 3 s 560 3580 0 )); DATA(insert ( 4072 702 702 4 s 565 3580 0 )); DATA(insert ( 4072 702 702 5 s 563 3580 0 )); +/* bloom abstime */ +DATA(insert ( 5031 702 702 1 s 560 3580 0 )); /* minmax reltime */ DATA(insert ( 4073 703 703 1 s 568 3580 0 )); DATA(insert ( 4073 703 703 2 s 570 3580 0 )); DATA(insert ( 4073 703 703 3 s 566 3580 0 )); DATA(insert ( 4073 703 703 4 s 571 3580 0 )); DATA(insert ( 4073 703 703 5 s 569 3580 0 )); +/* bloom reltime */ +DATA(insert ( 5032 703 703 1 s 566 3580 0 )); /* minmax macaddr */ DATA(insert ( 4074 829 829 1 s 1222 3580 0 )); DATA(insert ( 4074 829 829 2 s 1223 3580 0 )); DATA(insert ( 4074 829 829 3 s 1220 3580 0 )); DATA(insert ( 4074 829 829 4 s 1225 3580 0 )); DATA(insert ( 4074 829 829 5 s 1224 3580 0 )); +/* bloom macaddr */ +DATA(insert ( 5033 829 829 1 s 1220 3580 0 )); /* minmax macaddr8 */ DATA(insert ( 4109 774 774 1 s 3364 3580 0 )); DATA(insert ( 4109 774 774 2 s 3365 3580 0 )); DATA(insert ( 4109 774 774 3 s 3362 3580 0 )); DATA(insert ( 4109 774 774 4 s 3367 3580 0 )); DATA(insert ( 4109 774 774 5 s 3366 3580 0 )); +/* bloom macaddr8 */ +DATA(insert ( 5034 774 774 1 s 3362 3580 0 )); /* minmax inet */ DATA(insert ( 4075 869 869 1 s 1203 3580 0 )); DATA(insert ( 4075 869 869 2 s 1204 3580 0 )); @@ -1046,18 +1079,24 @@ DATA(insert ( 4102 869 869 8 s 932 3580 0 )); DATA(insert ( 4102 869 869 18 s 1201 3580 0 )); DATA(insert ( 4102 869 869 24 s 933 3580 0 )); DATA(insert ( 4102 869 869 26 s 931 3580 0 )); +/* bloom inet */ +DATA(insert ( 5035 869 869 1 s 1201 3580 0 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 s 1058 3580 0 )); DATA(insert ( 4076 1042 1042 2 s 1059 3580 0 )); DATA(insert ( 4076 1042 1042 3 s 1054 3580 0 )); DATA(insert ( 4076 1042 1042 4 s 1061 3580 0 )); DATA(insert ( 4076 1042 1042 5 s 1060 3580 0 )); +/* bloom character */ +DATA(insert ( 5036 1042 1042 1 s 1054 3580 0 )); /* minmax time without time zone */ DATA(insert ( 4077 1083 1083 1 s 1110 3580 0 )); DATA(insert ( 4077 1083 1083 2 s 1111 3580 0 )); DATA(insert ( 4077 1083 1083 3 s 1108 3580 0 )); DATA(insert ( 4077 1083 1083 4 s 1113 3580 0 )); DATA(insert ( 4077 1083 1083 5 s 1112 3580 0 )); +/* bloom time without time zone */ +DATA(insert ( 5037 1083 1083 1 s 1108 3580 0 )); /* minmax datetime (date, timestamp, timestamptz) */ DATA(insert ( 4059 1114 1114 1 s 2062 3580 0 )); DATA(insert ( 4059 1114 1114 2 s 2063 3580 0 )); @@ -1104,6 +1143,16 @@ DATA(insert ( 4059 1184 1184 2 s 1323 3580 0 )); DATA(insert ( 4059 1184 1184 3 s 1320 3580 0 )); DATA(insert ( 4059 1184 1184 4 s 1325 3580 0 )); DATA(insert ( 4059 1184 1184 5 s 1324 3580 0 )); +/* bloom datetime (date, timestamp, timestamptz) */ +DATA(insert ( 5038 1114 1114 1 s 2060 3580 0 )); +DATA(insert ( 5038 1114 1082 1 s 2373 3580 0 )); +DATA(insert ( 5038 1114 1184 1 s 2536 3580 0 )); +DATA(insert ( 5038 1082 1082 1 s 1093 3580 0 )); +DATA(insert ( 5038 1082 1114 1 s 2347 3580 0 )); +DATA(insert ( 5038 1082 1184 1 s 2360 3580 0 )); +DATA(insert ( 5038 1184 1082 1 s 2386 3580 0 )); +DATA(insert ( 5038 1184 1114 1 s 2542 3580 0 )); +DATA(insert ( 5038 1184 1184 1 s 1320 3580 0 )); /* minmax interval */ DATA(insert ( 4078 1186 1186 1 s 1332 3580 0 )); @@ -1111,12 +1160,16 @@ DATA(insert ( 4078 1186 1186 2 s 1333 3580 0 )); DATA(insert ( 4078 1186 1186 3 s 1330 3580 0 )); DATA(insert ( 4078 1186 1186 4 s 1335 3580 0 )); DATA(insert ( 4078 1186 1186 5 s 1334 3580 0 )); +/* bloom interval */ +DATA(insert ( 5041 1186 1186 1 s 1330 3580 0 )); /* minmax time with time zone */ DATA(insert ( 4058 1266 1266 1 s 1552 3580 0 )); DATA(insert ( 4058 1266 1266 2 s 1553 3580 0 )); DATA(insert ( 4058 1266 1266 3 s 1550 3580 0 )); DATA(insert ( 4058 1266 1266 4 s 1555 3580 0 )); DATA(insert ( 4058 1266 1266 5 s 1554 3580 0 )); +/* bloom time with time zone */ +DATA(insert ( 5042 1266 1266 1 s 1550 3580 0 )); /* minmax bit */ DATA(insert ( 4079 1560 1560 1 s 1786 3580 0 )); DATA(insert ( 4079 1560 1560 2 s 1788 3580 0 )); @@ -1135,12 +1188,16 @@ DATA(insert ( 4055 1700 1700 2 s 1755 3580 0 )); DATA(insert ( 4055 1700 1700 3 s 1752 3580 0 )); DATA(insert ( 4055 1700 1700 4 s 1757 3580 0 )); DATA(insert ( 4055 1700 1700 5 s 1756 3580 0 )); +/* bloom numeric */ +DATA(insert ( 5045 1700 1700 1 s 1752 3580 0 )); /* minmax uuid */ DATA(insert ( 4081 2950 2950 1 s 2974 3580 0 )); DATA(insert ( 4081 2950 2950 2 s 2976 3580 0 )); DATA(insert ( 4081 2950 2950 3 s 2972 3580 0 )); DATA(insert ( 4081 2950 2950 4 s 2977 3580 0 )); DATA(insert ( 4081 2950 2950 5 s 2975 3580 0 )); +/* bloom uuid */ +DATA(insert ( 5046 2950 2950 1 s 2972 3580 0 )); /* inclusion range types */ DATA(insert ( 4103 3831 3831 1 s 3893 3580 0 )); DATA(insert ( 4103 3831 3831 2 s 3895 3580 0 )); @@ -1162,6 +1219,8 @@ DATA(insert ( 4082 3220 3220 2 s 3226 3580 0 )); DATA(insert ( 4082 3220 3220 3 s 3222 3580 0 )); DATA(insert ( 4082 3220 3220 4 s 3227 3580 0 )); DATA(insert ( 4082 3220 3220 5 s 3225 3580 0 )); +/* bloom pg_lsn */ +DATA(insert ( 5047 3220 3220 1 s 3222 3580 0 )); /* inclusion box */ DATA(insert ( 4104 603 603 1 s 493 3580 0 )); DATA(insert ( 4104 603 603 2 s 494 3580 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index f545a058..4871836 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -347,16 +347,34 @@ DATA(insert ( 4064 17 17 1 3383 )); DATA(insert ( 4064 17 17 2 3384 )); DATA(insert ( 4064 17 17 3 3385 )); DATA(insert ( 4064 17 17 4 3386 )); +/* bloom bytea */ +DATA(insert ( 5021 17 17 1 5017 )); +DATA(insert ( 5021 17 17 2 5018 )); +DATA(insert ( 5021 17 17 3 5019 )); +DATA(insert ( 5021 17 17 4 5020 )); +DATA(insert ( 5021 17 17 11 456 )); /* minmax "char" */ DATA(insert ( 4062 18 18 1 3383 )); DATA(insert ( 4062 18 18 2 3384 )); DATA(insert ( 4062 18 18 3 3385 )); DATA(insert ( 4062 18 18 4 3386 )); +/* bloom "char" */ +DATA(insert ( 5022 18 18 1 5017 )); +DATA(insert ( 5022 18 18 2 5018 )); +DATA(insert ( 5022 18 18 3 5019 )); +DATA(insert ( 5022 18 18 4 5020 )); +DATA(insert ( 5022 18 18 11 454 )); /* minmax name */ DATA(insert ( 4065 19 19 1 3383 )); DATA(insert ( 4065 19 19 2 3384 )); DATA(insert ( 4065 19 19 3 3385 )); DATA(insert ( 4065 19 19 4 3386 )); +/* bloom name */ +DATA(insert ( 5023 19 19 1 5017 )); +DATA(insert ( 5023 19 19 2 5018 )); +DATA(insert ( 5023 19 19 3 5019 )); +DATA(insert ( 5023 19 19 4 5020 )); +DATA(insert ( 5023 19 19 11 455 )); /* minmax integer: int2, int4, int8 */ DATA(insert ( 4054 20 20 1 3383 )); DATA(insert ( 4054 20 20 2 3384 )); @@ -397,16 +415,47 @@ DATA(insert ( 4054 23 21 2 3384 )); DATA(insert ( 4054 23 21 3 3385 )); DATA(insert ( 4054 23 21 4 3386 )); +/* bloom integer: int2, int4, int8 */ +DATA(insert ( 5024 20 20 1 5017 )); +DATA(insert ( 5024 20 20 2 5018 )); +DATA(insert ( 5024 20 20 3 5019 )); +DATA(insert ( 5024 20 20 4 5020 )); +DATA(insert ( 5024 20 20 11 949 )); + +DATA(insert ( 5024 21 21 1 5017 )); +DATA(insert ( 5024 21 21 2 5018 )); +DATA(insert ( 5024 21 21 3 5019 )); +DATA(insert ( 5024 21 21 4 5020 )); +DATA(insert ( 5024 21 21 11 449 )); + +DATA(insert ( 5024 23 23 1 5017 )); +DATA(insert ( 5024 23 23 2 5018 )); +DATA(insert ( 5024 23 23 3 5019 )); +DATA(insert ( 5024 23 23 4 5020 )); +DATA(insert ( 5024 23 23 11 450 )); + /* minmax text */ DATA(insert ( 4056 25 25 1 3383 )); DATA(insert ( 4056 25 25 2 3384 )); DATA(insert ( 4056 25 25 3 3385 )); DATA(insert ( 4056 25 25 4 3386 )); +/* bloom text */ +DATA(insert ( 5027 25 25 1 5017 )); +DATA(insert ( 5027 25 25 2 5018 )); +DATA(insert ( 5027 25 25 3 5019 )); +DATA(insert ( 5027 25 25 4 5020 )); +DATA(insert ( 5027 25 25 11 400 )); /* minmax oid */ DATA(insert ( 4068 26 26 1 3383 )); DATA(insert ( 4068 26 26 2 3384 )); DATA(insert ( 4068 26 26 3 3385 )); DATA(insert ( 4068 26 26 4 3386 )); +/* bloom oid */ +DATA(insert ( 5029 26 26 1 5017 )); +DATA(insert ( 5029 26 26 2 5018 )); +DATA(insert ( 5029 26 26 3 5019 )); +DATA(insert ( 5029 26 26 4 5020 )); +DATA(insert ( 5029 26 26 11 453 )); /* minmax tid */ DATA(insert ( 4069 27 27 1 3383 )); DATA(insert ( 4069 27 27 2 3384 )); @@ -433,26 +482,63 @@ DATA(insert ( 4070 701 700 2 3384 )); DATA(insert ( 4070 701 700 3 3385 )); DATA(insert ( 4070 701 700 4 3386 )); +/* bloom float */ +DATA(insert ( 5030 700 700 1 5017 )); +DATA(insert ( 5030 700 700 2 5018 )); +DATA(insert ( 5030 700 700 3 5019 )); +DATA(insert ( 5030 700 700 4 5020 )); +DATA(insert ( 5030 700 700 11 451 )); + +DATA(insert ( 5030 701 701 1 5017 )); +DATA(insert ( 5030 701 701 2 5018 )); +DATA(insert ( 5030 701 701 3 5019 )); +DATA(insert ( 5030 701 701 4 5020 )); +DATA(insert ( 5030 701 701 11 452 )); + /* minmax abstime */ DATA(insert ( 4072 702 702 1 3383 )); DATA(insert ( 4072 702 702 2 3384 )); DATA(insert ( 4072 702 702 3 3385 )); DATA(insert ( 4072 702 702 4 3386 )); +/* bloom abstime */ +DATA(insert ( 5031 702 702 1 5017 )); +DATA(insert ( 5031 702 702 2 5018 )); +DATA(insert ( 5031 702 702 3 5019 )); +DATA(insert ( 5031 702 702 4 5020 )); +DATA(insert ( 5031 702 702 11 450 )); /* minmax reltime */ DATA(insert ( 4073 703 703 1 3383 )); DATA(insert ( 4073 703 703 2 3384 )); DATA(insert ( 4073 703 703 3 3385 )); DATA(insert ( 4073 703 703 4 3386 )); +/* bloom reltime */ +DATA(insert ( 5032 703 703 1 5017 )); +DATA(insert ( 5032 703 703 2 5018 )); +DATA(insert ( 5032 703 703 3 5019 )); +DATA(insert ( 5032 703 703 4 5020 )); +DATA(insert ( 5032 703 703 11 450 )); /* minmax macaddr */ DATA(insert ( 4074 829 829 1 3383 )); DATA(insert ( 4074 829 829 2 3384 )); DATA(insert ( 4074 829 829 3 3385 )); DATA(insert ( 4074 829 829 4 3386 )); +/* bloom macaddr */ +DATA(insert ( 5033 829 829 1 5017 )); +DATA(insert ( 5033 829 829 2 5018 )); +DATA(insert ( 5033 829 829 3 5019 )); +DATA(insert ( 5033 829 829 4 5020 )); +DATA(insert ( 5033 829 829 11 399 )); /* minmax macaddr8 */ DATA(insert ( 4109 774 774 1 3383 )); DATA(insert ( 4109 774 774 2 3384 )); DATA(insert ( 4109 774 774 3 3385 )); DATA(insert ( 4109 774 774 4 3386 )); +/* bloom macaddr8 */ +DATA(insert ( 5034 774 774 1 5017 )); +DATA(insert ( 5034 774 774 2 5018 )); +DATA(insert ( 5034 774 774 3 5019 )); +DATA(insert ( 5034 774 774 4 5020 )); +DATA(insert ( 5034 774 774 11 328 )); /* minmax inet */ DATA(insert ( 4075 869 869 1 3383 )); DATA(insert ( 4075 869 869 2 3384 )); @@ -466,16 +552,34 @@ DATA(insert ( 4102 869 869 4 4108 )); DATA(insert ( 4102 869 869 11 4063 )); DATA(insert ( 4102 869 869 12 4071 )); DATA(insert ( 4102 869 869 13 930 )); +/* bloom inet */ +DATA(insert ( 5035 869 869 1 5017 )); +DATA(insert ( 5035 869 869 2 5018 )); +DATA(insert ( 5035 869 869 3 5019 )); +DATA(insert ( 5035 869 869 4 5020 )); +DATA(insert ( 5035 869 869 11 422 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 3383 )); DATA(insert ( 4076 1042 1042 2 3384 )); DATA(insert ( 4076 1042 1042 3 3385 )); DATA(insert ( 4076 1042 1042 4 3386 )); +/* bloom character */ +DATA(insert ( 5036 1042 1042 1 5017 )); +DATA(insert ( 5036 1042 1042 2 5018 )); +DATA(insert ( 5036 1042 1042 3 5019 )); +DATA(insert ( 5036 1042 1042 4 5020 )); +DATA(insert ( 5036 1042 1042 11 454 )); /* minmax time without time zone */ DATA(insert ( 4077 1083 1083 1 3383 )); DATA(insert ( 4077 1083 1083 2 3384 )); DATA(insert ( 4077 1083 1083 3 3385 )); DATA(insert ( 4077 1083 1083 4 3386 )); +/* bloom time without time zone */ +DATA(insert ( 5037 1083 1083 1 5017 )); +DATA(insert ( 5037 1083 1083 2 5018 )); +DATA(insert ( 5037 1083 1083 3 5019 )); +DATA(insert ( 5037 1083 1083 4 5020 )); +DATA(insert ( 5037 1083 1083 11 1688 )); /* minmax datetime (date, timestamp, timestamptz) */ DATA(insert ( 4059 1114 1114 1 3383 )); DATA(insert ( 4059 1114 1114 2 3384 )); @@ -516,16 +620,47 @@ DATA(insert ( 4059 1082 1184 2 3384 )); DATA(insert ( 4059 1082 1184 3 3385 )); DATA(insert ( 4059 1082 1184 4 3386 )); +/* bloom datetime (date, timestamp, timestamptz) */ +DATA(insert ( 5038 1114 1114 1 5017 )); +DATA(insert ( 5038 1114 1114 2 5018 )); +DATA(insert ( 5038 1114 1114 3 5019 )); +DATA(insert ( 5038 1114 1114 4 5020 )); +DATA(insert ( 5038 1114 1114 11 2039 )); + +DATA(insert ( 5038 1184 1184 1 5017 )); +DATA(insert ( 5038 1184 1184 2 5018 )); +DATA(insert ( 5038 1184 1184 3 5019 )); +DATA(insert ( 5038 1184 1184 4 5020 )); +DATA(insert ( 5038 1184 1184 11 2039 )); + +DATA(insert ( 5038 1082 1082 1 5017 )); +DATA(insert ( 5038 1082 1082 2 5018 )); +DATA(insert ( 5038 1082 1082 3 5019 )); +DATA(insert ( 5038 1082 1082 4 5020 )); +DATA(insert ( 5038 1082 1082 11 450 )); + /* minmax interval */ DATA(insert ( 4078 1186 1186 1 3383 )); DATA(insert ( 4078 1186 1186 2 3384 )); DATA(insert ( 4078 1186 1186 3 3385 )); DATA(insert ( 4078 1186 1186 4 3386 )); +/* bloom interval */ +DATA(insert ( 5041 1186 1186 1 5017 )); +DATA(insert ( 5041 1186 1186 2 5018 )); +DATA(insert ( 5041 1186 1186 3 5019 )); +DATA(insert ( 5041 1186 1186 4 5020 )); +DATA(insert ( 5041 1186 1186 11 1697 )); /* minmax time with time zone */ DATA(insert ( 4058 1266 1266 1 3383 )); DATA(insert ( 4058 1266 1266 2 3384 )); DATA(insert ( 4058 1266 1266 3 3385 )); DATA(insert ( 4058 1266 1266 4 3386 )); +/* bloom time with time zone */ +DATA(insert ( 5042 1266 1266 1 5017 )); +DATA(insert ( 5042 1266 1266 2 5018 )); +DATA(insert ( 5042 1266 1266 3 5019 )); +DATA(insert ( 5042 1266 1266 4 5020 )); +DATA(insert ( 5042 1266 1266 11 1696 )); /* minmax bit */ DATA(insert ( 4079 1560 1560 1 3383 )); DATA(insert ( 4079 1560 1560 2 3384 )); @@ -541,11 +676,23 @@ DATA(insert ( 4055 1700 1700 1 3383 )); DATA(insert ( 4055 1700 1700 2 3384 )); DATA(insert ( 4055 1700 1700 3 3385 )); DATA(insert ( 4055 1700 1700 4 3386 )); +/* bloom numeric */ +DATA(insert ( 5045 1700 1700 1 5017 )); +DATA(insert ( 5045 1700 1700 2 5018 )); +DATA(insert ( 5045 1700 1700 3 5019 )); +DATA(insert ( 5045 1700 1700 4 5020 )); +DATA(insert ( 5045 1700 1700 11 432 )); /* minmax uuid */ DATA(insert ( 4081 2950 2950 1 3383 )); DATA(insert ( 4081 2950 2950 2 3384 )); DATA(insert ( 4081 2950 2950 3 3385 )); DATA(insert ( 4081 2950 2950 4 3386 )); +/* bloom uuid */ +DATA(insert ( 5046 2950 2950 1 5017 )); +DATA(insert ( 5046 2950 2950 2 5018 )); +DATA(insert ( 5046 2950 2950 3 5019 )); +DATA(insert ( 5046 2950 2950 4 5020 )); +DATA(insert ( 5046 2950 2950 11 2963 )); /* inclusion range types */ DATA(insert ( 4103 3831 3831 1 4105 )); DATA(insert ( 4103 3831 3831 2 4106 )); @@ -559,6 +706,12 @@ DATA(insert ( 4082 3220 3220 1 3383 )); DATA(insert ( 4082 3220 3220 2 3384 )); DATA(insert ( 4082 3220 3220 3 3385 )); DATA(insert ( 4082 3220 3220 4 3386 )); +/* bloom pg_lsn */ +DATA(insert ( 5047 3220 3220 1 5017 )); +DATA(insert ( 5047 3220 3220 2 5018 )); +DATA(insert ( 5047 3220 3220 3 5019 )); +DATA(insert ( 5047 3220 3220 4 5020 )); +DATA(insert ( 5047 3220 3220 11 3252 )); /* inclusion box */ DATA(insert ( 4104 603 603 1 4105 )); DATA(insert ( 4104 603 603 2 4106 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index e11d52e..7dbd4ff 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -214,36 +214,61 @@ DATA(insert ( 2742 jsonb_path_ops PGNSP PGUID 4037 3802 f 23 )); /* BRIN operator classes */ /* no brin opclass for bool */ DATA(insert ( 3580 bytea_minmax_ops PGNSP PGUID 4064 17 t 17 )); +DATA(insert ( 3580 bytea_bloom_ops PGNSP PGUID 5021 17 f 17 )); DATA(insert ( 3580 char_minmax_ops PGNSP PGUID 4062 18 t 18 )); +DATA(insert ( 3580 char_bloom_ops PGNSP PGUID 5022 18 f 18 )); DATA(insert ( 3580 name_minmax_ops PGNSP PGUID 4065 19 t 19 )); +DATA(insert ( 3580 name_bloom_ops PGNSP PGUID 5023 19 f 19 )); DATA(insert ( 3580 int8_minmax_ops PGNSP PGUID 4054 20 t 20 )); +DATA(insert ( 3580 int8_bloom_ops PGNSP PGUID 5024 20 f 20 )); DATA(insert ( 3580 int2_minmax_ops PGNSP PGUID 4054 21 t 21 )); +DATA(insert ( 3580 int2_bloom_ops PGNSP PGUID 5024 21 f 21 )); DATA(insert ( 3580 int4_minmax_ops PGNSP PGUID 4054 23 t 23 )); +DATA(insert ( 3580 int4_bloom_ops PGNSP PGUID 5024 23 f 23 )); DATA(insert ( 3580 text_minmax_ops PGNSP PGUID 4056 25 t 25 )); +DATA(insert ( 3580 text_bloom_ops PGNSP PGUID 5027 25 f 25 )); DATA(insert ( 3580 oid_minmax_ops PGNSP PGUID 4068 26 t 26 )); +DATA(insert ( 3580 oid_bloom_ops PGNSP PGUID 5029 26 f 26 )); DATA(insert ( 3580 tid_minmax_ops PGNSP PGUID 4069 27 t 27 )); DATA(insert ( 3580 float4_minmax_ops PGNSP PGUID 4070 700 t 700 )); +DATA(insert ( 3580 float4_bloom_ops PGNSP PGUID 5030 700 f 700 )); DATA(insert ( 3580 float8_minmax_ops PGNSP PGUID 4070 701 t 701 )); +DATA(insert ( 3580 float8_bloom_ops PGNSP PGUID 5030 701 f 701 )); DATA(insert ( 3580 abstime_minmax_ops PGNSP PGUID 4072 702 t 702 )); +DATA(insert ( 3580 abstime_bloom_ops PGNSP PGUID 5031 702 f 702 )); DATA(insert ( 3580 reltime_minmax_ops PGNSP PGUID 4073 703 t 703 )); +DATA(insert ( 3580 reltime_bloom_ops PGNSP PGUID 5032 703 f 703 )); DATA(insert ( 3580 macaddr_minmax_ops PGNSP PGUID 4074 829 t 829 )); +DATA(insert ( 3580 macaddr_bloom_ops PGNSP PGUID 5033 829 f 829 )); DATA(insert ( 3580 macaddr8_minmax_ops PGNSP PGUID 4109 774 t 774 )); +DATA(insert ( 3580 macaddr8_bloom_ops PGNSP PGUID 5034 774 f 774 )); DATA(insert ( 3580 inet_minmax_ops PGNSP PGUID 4075 869 f 869 )); DATA(insert ( 3580 inet_inclusion_ops PGNSP PGUID 4102 869 t 869 )); +DATA(insert ( 3580 inet_bloom_ops PGNSP PGUID 5035 869 f 869 )); DATA(insert ( 3580 bpchar_minmax_ops PGNSP PGUID 4076 1042 t 1042 )); +DATA(insert ( 3580 bpchar_bloom_ops PGNSP PGUID 5036 1042 f 1042 )); DATA(insert ( 3580 time_minmax_ops PGNSP PGUID 4077 1083 t 1083 )); +DATA(insert ( 3580 time_bloom_ops PGNSP PGUID 5037 1083 f 1083 )); DATA(insert ( 3580 date_minmax_ops PGNSP PGUID 4059 1082 t 1082 )); +DATA(insert ( 3580 date_bloom_ops PGNSP PGUID 5038 1082 f 1082 )); DATA(insert ( 3580 timestamp_minmax_ops PGNSP PGUID 4059 1114 t 1114 )); +DATA(insert ( 3580 timestamp_bloom_ops PGNSP PGUID 5038 1114 f 1114 )); DATA(insert ( 3580 timestamptz_minmax_ops PGNSP PGUID 4059 1184 t 1184 )); +DATA(insert ( 3580 timestamptz_bloom_ops PGNSP PGUID 5038 1184 f 1184 )); DATA(insert ( 3580 interval_minmax_ops PGNSP PGUID 4078 1186 t 1186 )); +DATA(insert ( 3580 interval_bloom_ops PGNSP PGUID 5041 1186 f 1186 )); DATA(insert ( 3580 timetz_minmax_ops PGNSP PGUID 4058 1266 t 1266 )); +DATA(insert ( 3580 timetz_bloom_ops PGNSP PGUID 5042 1266 f 1266 )); DATA(insert ( 3580 bit_minmax_ops PGNSP PGUID 4079 1560 t 1560 )); DATA(insert ( 3580 varbit_minmax_ops PGNSP PGUID 4080 1562 t 1562 )); DATA(insert ( 3580 numeric_minmax_ops PGNSP PGUID 4055 1700 t 1700 )); +DATA(insert ( 3580 numeric_bloom_ops PGNSP PGUID 5045 1700 f 1700 )); /* no brin opclass for record, anyarray */ DATA(insert ( 3580 uuid_minmax_ops PGNSP PGUID 4081 2950 t 2950 )); +DATA(insert ( 3580 uuid_bloom_ops PGNSP PGUID 5046 2950 f 2950 )); DATA(insert ( 3580 range_inclusion_ops PGNSP PGUID 4103 3831 t 3831 )); DATA(insert ( 3580 pg_lsn_minmax_ops PGNSP PGUID 4082 3220 t 3220 )); +DATA(insert ( 3580 pg_lsn_bloom_ops PGNSP PGUID 5047 3220 f 3220 )); /* no brin opclass for enum, tsvector, tsquery, jsonb */ DATA(insert ( 3580 box_inclusion_ops PGNSP PGUID 4104 603 t 603 )); /* no brin opclass for the geometric types except box */ diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index b544474..bc22ce3 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -160,30 +160,50 @@ DATA(insert OID = 4036 ( 2742 jsonb_ops PGNSP PGUID )); DATA(insert OID = 4037 ( 2742 jsonb_path_ops PGNSP PGUID )); DATA(insert OID = 4054 ( 3580 integer_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5024 ( 3580 integer_bloom_ops PGNSP PGUID )); DATA(insert OID = 4055 ( 3580 numeric_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5045 ( 3580 numeric_bloom_ops PGNSP PGUID )); DATA(insert OID = 4056 ( 3580 text_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5027 ( 3580 text_bloom_ops PGNSP PGUID )); DATA(insert OID = 4058 ( 3580 timetz_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5042 ( 3580 timetz_bloom_ops PGNSP PGUID )); DATA(insert OID = 4059 ( 3580 datetime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5038 ( 3580 datetime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4062 ( 3580 char_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5022 ( 3580 char_bloom_ops PGNSP PGUID )); DATA(insert OID = 4064 ( 3580 bytea_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5021 ( 3580 bytea_bloom_ops PGNSP PGUID )); DATA(insert OID = 4065 ( 3580 name_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5023 ( 3580 name_bloom_ops PGNSP PGUID )); DATA(insert OID = 4068 ( 3580 oid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5029 ( 3580 oid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4069 ( 3580 tid_minmax_ops PGNSP PGUID )); DATA(insert OID = 4070 ( 3580 float_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5030 ( 3580 float_bloom_ops PGNSP PGUID )); DATA(insert OID = 4072 ( 3580 abstime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5031 ( 3580 abstime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4073 ( 3580 reltime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5032 ( 3580 reltime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4074 ( 3580 macaddr_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5033 ( 3580 macaddr_bloom_ops PGNSP PGUID )); DATA(insert OID = 4109 ( 3580 macaddr8_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5034 ( 3580 macaddr8_bloom_ops PGNSP PGUID )); DATA(insert OID = 4075 ( 3580 network_minmax_ops PGNSP PGUID )); DATA(insert OID = 4102 ( 3580 network_inclusion_ops PGNSP PGUID )); +DATA(insert OID = 5035 ( 3580 network_bloom_ops PGNSP PGUID )); DATA(insert OID = 4076 ( 3580 bpchar_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5036 ( 3580 bpchar_bloom_ops PGNSP PGUID )); DATA(insert OID = 4077 ( 3580 time_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5037 ( 3580 time_bloom_ops PGNSP PGUID )); DATA(insert OID = 4078 ( 3580 interval_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5041 ( 3580 interval_bloom_ops PGNSP PGUID )); DATA(insert OID = 4079 ( 3580 bit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4080 ( 3580 varbit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4081 ( 3580 uuid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5046 ( 3580 uuid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4103 ( 3580 range_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); +DATA(insert OID = 5047 ( 3580 pg_lsn_bloom_ops PGNSP PGUID )); DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID )); DATA(insert OID = 5008 ( 4000 poly_ops PGNSP PGUID )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f01648c..7154e5e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4374,6 +4374,16 @@ DESCR("BRIN inclusion support"); DATA(insert OID = 4108 ( brin_inclusion_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_union _null_ _null_ _null_ )); DESCR("BRIN inclusion support"); +/* BRIN bloom */ +DATA(insert OID = 5017 ( brin_bloom_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_opcinfo _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5018 ( brin_bloom_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_add_value _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5019 ( brin_bloom_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_consistent _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); +DATA(insert OID = 5020 ( brin_bloom_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_bloom_union _null_ _null_ _null_ )); +DESCR("BRIN bloom support"); + /* userlock replacements */ DATA(insert OID = 2880 ( pg_advisory_lock PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 2278 "20" _null_ _null_ _null_ _null_ _null_ pg_advisory_lock_int8 _null_ _null_ _null_ )); DESCR("obtain exclusive advisory lock"); diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000..ec6e27a --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,438 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 684f7f2..91b2ecc 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1819,6 +1819,7 @@ ORDER BY 1, 2, 3; 2742 | 11 | ?& 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -1880,7 +1881,7 @@ ORDER BY 1, 2, 3; 4000 | 25 | <<= 4000 | 26 | >> 4000 | 27 | >>= -(121 rows) +(122 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index ad9434f..149999d 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- # Another group of parallel tests # ---------- -test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password +test: brin brin_bloom gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 27cd498..06c3488 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -106,6 +106,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000..8b2c005 --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,391 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.9.5
0004-BRIN-multi-range-minmax-indexes.patch (text/x-patch, 146.8K) download From 871bab2388b79b5d4c3de2bb008a762ed2246986 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Tue, 9 Jan 2018 01:01:26 +0100 Subject: [PATCH 4/4] BRIN multi-range minmax indexes The built-in minmax opclass works well for column correlated with physical location in the table (e.g. timestamp in append-only tables). But when the data is not perfectly correlated (possibly due to later DELETE/UPDATE/INSERT commands), the minmax ranges get very wide and ineffective to eliminate the page ranges. This opclass deals with that by replacing the one [min,max] range with multiple smaller ones. This allows us to track gaps, and deal with outlier values. Note: Currently, this only works with float8-based data types. Supporting additional data types is not a big issue, but will require extending the opclass with "subtract" operator (used to compute distance between values when merging ranges). --- doc/src/sgml/brin.sgml | 234 ++- src/backend/access/brin/Makefile | 3 +- src/backend/access/brin/brin_minmax_multi.c | 2200 +++++++++++++++++++++++++++ src/include/catalog/pg_amop.h | 191 +++ src/include/catalog/pg_amproc.h | 201 +++ src/include/catalog/pg_opclass.h | 21 + src/include/catalog/pg_opfamily.h | 16 + src/include/catalog/pg_proc.h | 44 + src/test/regress/expected/brin_multi.out | 408 +++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/brin_multi.sql | 361 +++++ 11 files changed, 3672 insertions(+), 9 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 434538e..184c886 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -136,6 +136,17 @@ </entry> </row> <row> + <entry><literal>abstime_minmax_multi_ops</literal></entry> + <entry><type>abstime</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>int8_bloom_ops</literal></entry> <entry><type>bigint</type></entry> <entry> @@ -266,6 +277,17 @@ </entry> </row> <row> + <entry><literal>date_minmax_multi_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>float8_bloom_ops</literal></entry> <entry><type>double precision</type></entry> <entry> @@ -284,6 +306,17 @@ </entry> </row> <row> + <entry><literal>float8_minmax_multi_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>inet_bloom_ops</literal></entry> <entry><type>inet</type></entry> <entry> @@ -302,6 +335,17 @@ </entry> </row> <row> + <entry><literal>inet_minmax_multi_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>network_inclusion_ops</literal></entry> <entry><type>inet</type></entry> <entry> @@ -350,6 +394,17 @@ </entry> </row> <row> + <entry><literal>interval_minmax_multi_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>macaddr_bloom_ops</literal></entry> <entry><type>macaddr</type></entry> <entry> @@ -368,6 +423,17 @@ </entry> </row> <row> + <entry><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>macaddr8_bloom_ops</literal></entry> <entry><type>macaddr8</type></entry> <entry> @@ -386,6 +452,17 @@ </entry> </row> <row> + <entry><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>name_bloom_ops</literal></entry> <entry><type>name</type></entry> <entry> @@ -440,6 +517,17 @@ </entry> </row> <row> + <entry><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>oid_bloom_ops</literal></entry> <entry><type>oid</type></entry> <entry> @@ -514,6 +602,17 @@ </entry> </row> <row> + <entry><literal>reltime_minmax_multi_ops</literal></entry> + <entry><type>reltime</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>int2_bloom_ops</literal></entry> <entry><type>smallint</type></entry> <entry> @@ -579,6 +678,17 @@ </entry> </row> <row> + <entry><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>timestamptz_bloom_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> <entry> @@ -597,6 +707,17 @@ </entry> </row> <row> + <entry><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>time_bloom_ops</literal></entry> <entry><type>time without time zone</type></entry> <entry> @@ -615,6 +736,17 @@ </entry> </row> <row> + <entry><literal>time_minmax_multi_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>timetz_bloom_ops</literal></entry> <entry><type>time with time zone</type></entry> <entry> @@ -633,6 +765,17 @@ </entry> </row> <row> + <entry><literal>timetz_minmax_multi_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> + <row> <entry><literal>uuid_bloom_ops</literal></entry> <entry><type>uuid</type></entry> <entry> @@ -650,6 +793,17 @@ <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_minmax_multi_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> </tbody> </tgroup> </table> @@ -744,13 +898,13 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support procedures' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions using + them are shipped for in-core data types as appropriate. Additional operator + classes can be defined by the user for other data types using equivalent + definitions, without having to write any source code; appropriate catalog + entries being declared is enough. Note that assumptions about the semantics + of operator strategies are embedded in the support procedures' source code. </para> <para> @@ -820,6 +974,72 @@ typedef struct BrinOpcInfo </table> <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> To write an operator class for a complex data type which has values included within another type, it's possible to use the inclusion support procedures alongside the corresponding operators, as shown diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index a76d927..c87c796 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,7 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o \ + brin_minmax_multi.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000..cdb2857 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2200 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see MINMAX_MAX_VALUES). Collapsed intervals (with equal + * minimum and maximum value) are stored as a single value, while regular + * intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of MINMAX_MAX_VALUES), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/nabstime.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Maximum number of values (individual points or range boundaries) to + * keep in the summary values. + */ +#define MINMAX_MAX_VALUES 64 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than MINMAX_MAX_VALUES values - if needed we perform + * compaction by merging some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for MINMAX_MAX_VALUES elements. This should be fine as long as + * the MINMAX_MAX_VALUES is reasonably small (64 seems fine). + */ +typedef struct Ranges +{ + /* (2*nranges + nvalues) <= MINMAX_MAX_VALUES */ + int nranges; /* number of ranges in the array (stored) */ + int nvalues; /* number of values in the data array (all) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (2*nranges + nvalues) <= MINMAX_MAX_VALUES */ + int nranges; /* number of ranges in the array (stored) */ + int nvalues; /* number of values in the data array (all) */ + + /* contains the actual data */ + char data[FLEXIBLE_ARRAY_MEMBER]; +} SerializedRanges; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(void) +{ + Size len; + + len = offsetof(Ranges, values); /* fixed header */ + len += MINMAX_MAX_VALUES * sizeof(Datum); /* Datum values */ + + return (Ranges *) palloc0(len); +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= MINMAX_MAX_VALUES); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range = minmax_multi_init(); + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= MINMAX_MAX_VALUES); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (MINMAX_MAX_VALUES). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= MINMAX_MAX_VALUES * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= MINMAX_MAX_VALUES); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit MINMAX_MAX_VALUES, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when MINMAX_MAX_VALUES is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < MINMAX_MAX_VALUES) + { + FmgrInfo *cmpFn; + Datum *values; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances); + + Assert(count_values(cranges, ncranges) <= MINMAX_MAX_VALUES * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + + +/* + * Compute distance between two abstime values. + * + * abstime values are int32 values, so just subtract them + */ +Datum +brin_minmax_multi_distance_abstime(PG_FUNCTION_ARGS) +{ + double delta; + + AbsoluteTime a = PG_GETARG_ABSOLUTETIME(0); + AbsoluteTime b = PG_GETARG_ABSOLUTETIME(1); + + delta = ((double)b - (double)a); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + + +/* + * Compute distance between two reltime values. + * + * reltime values are int32 values, so just subtract them + */ +Datum +brin_minmax_multi_distance_reltime(PG_FUNCTION_ARGS) +{ + double delta; + + RelativeTime a = PG_GETARG_ABSOLUTETIME(0); + RelativeTime b = PG_GETARG_ABSOLUTETIME(1); + + delta = ((double)b - (double)a); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + + +/* + * Examine the given index tuple (which contains 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_GETARG_DATUM(3); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + + /* Adjust "hasnulls" */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do */ + if (col_b->bv_allnulls) + PG_RETURN_VOID(); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * Adjust "allnulls". If A doesn't have values, just copy the values from + * B into A, and we're done. We cannot run the operators in this case, + * because values in A might contain garbage. Note we already established + * that B contains values. + */ + if (col_a->bv_allnulls) + { + col_a->bv_allnulls = false; + col_a->bv_values[0] = datumCopy(col_b->bv_values[0], false, -1); + PG_RETURN_VOID(); + } + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the MINMAX_MAX_VALUES + * threshold. The collapsed ranges will be stored as a single value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 84dc6e3..be2eba8 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -974,6 +974,52 @@ DATA(insert ( 4054 23 20 2 s 80 3580 0 )); DATA(insert ( 4054 23 20 3 s 15 3580 0 )); DATA(insert ( 4054 23 20 4 s 82 3580 0 )); DATA(insert ( 4054 23 20 5 s 76 3580 0 )); +/* minmax multi integer */ +DATA(insert ( 4126 20 20 1 s 412 3580 0 )); +DATA(insert ( 4126 20 20 2 s 414 3580 0 )); +DATA(insert ( 4126 20 20 3 s 410 3580 0 )); +DATA(insert ( 4126 20 20 4 s 415 3580 0 )); +DATA(insert ( 4126 20 20 5 s 413 3580 0 )); +DATA(insert ( 4126 20 21 1 s 1870 3580 0 )); +DATA(insert ( 4126 20 21 2 s 1872 3580 0 )); +DATA(insert ( 4126 20 21 3 s 1868 3580 0 )); +DATA(insert ( 4126 20 21 4 s 1873 3580 0 )); +DATA(insert ( 4126 20 21 5 s 1871 3580 0 )); +DATA(insert ( 4126 20 23 1 s 418 3580 0 )); +DATA(insert ( 4126 20 23 2 s 420 3580 0 )); +DATA(insert ( 4126 20 23 3 s 416 3580 0 )); +DATA(insert ( 4126 20 23 4 s 430 3580 0 )); +DATA(insert ( 4126 20 23 5 s 419 3580 0 )); +DATA(insert ( 4126 21 21 1 s 95 3580 0 )); +DATA(insert ( 4126 21 21 2 s 522 3580 0 )); +DATA(insert ( 4126 21 21 3 s 94 3580 0 )); +DATA(insert ( 4126 21 21 4 s 524 3580 0 )); +DATA(insert ( 4126 21 21 5 s 520 3580 0 )); +DATA(insert ( 4126 21 20 1 s 1864 3580 0 )); +DATA(insert ( 4126 21 20 2 s 1866 3580 0 )); +DATA(insert ( 4126 21 20 3 s 1862 3580 0 )); +DATA(insert ( 4126 21 20 4 s 1867 3580 0 )); +DATA(insert ( 4126 21 20 5 s 1865 3580 0 )); +DATA(insert ( 4126 21 23 1 s 534 3580 0 )); +DATA(insert ( 4126 21 23 2 s 540 3580 0 )); +DATA(insert ( 4126 21 23 3 s 532 3580 0 )); +DATA(insert ( 4126 21 23 4 s 542 3580 0 )); +DATA(insert ( 4126 21 23 5 s 536 3580 0 )); +DATA(insert ( 4126 23 23 1 s 97 3580 0 )); +DATA(insert ( 4126 23 23 2 s 523 3580 0 )); +DATA(insert ( 4126 23 23 3 s 96 3580 0 )); +DATA(insert ( 4126 23 23 4 s 525 3580 0 )); +DATA(insert ( 4126 23 23 5 s 521 3580 0 )); +DATA(insert ( 4126 23 21 1 s 535 3580 0 )); +DATA(insert ( 4126 23 21 2 s 541 3580 0 )); +DATA(insert ( 4126 23 21 3 s 533 3580 0 )); +DATA(insert ( 4126 23 21 4 s 543 3580 0 )); +DATA(insert ( 4126 23 21 5 s 537 3580 0 )); +DATA(insert ( 4126 23 20 1 s 37 3580 0 )); +DATA(insert ( 4126 23 20 2 s 80 3580 0 )); +DATA(insert ( 4126 23 20 3 s 15 3580 0 )); +DATA(insert ( 4126 23 20 4 s 82 3580 0 )); +DATA(insert ( 4126 23 20 5 s 76 3580 0 )); /* bloom integer */ DATA(insert ( 5024 20 20 1 s 410 3580 0 )); DATA(insert ( 5024 20 21 1 s 1868 3580 0 )); @@ -999,6 +1045,12 @@ DATA(insert ( 4068 26 26 2 s 611 3580 0 )); DATA(insert ( 4068 26 26 3 s 607 3580 0 )); DATA(insert ( 4068 26 26 4 s 612 3580 0 )); DATA(insert ( 4068 26 26 5 s 610 3580 0 )); +/* minmax oid */ +DATA(insert ( 4127 26 26 1 s 609 3580 0 )); +DATA(insert ( 4127 26 26 2 s 611 3580 0 )); +DATA(insert ( 4127 26 26 3 s 607 3580 0 )); +DATA(insert ( 4127 26 26 4 s 612 3580 0 )); +DATA(insert ( 4127 26 26 5 s 610 3580 0 )); /* bloom oid */ DATA(insert ( 5029 26 26 1 s 607 3580 0 )); /* minmax tid */ @@ -1007,6 +1059,12 @@ DATA(insert ( 4069 27 27 2 s 2801 3580 0 )); DATA(insert ( 4069 27 27 3 s 387 3580 0 )); DATA(insert ( 4069 27 27 4 s 2802 3580 0 )); DATA(insert ( 4069 27 27 5 s 2800 3580 0 )); +/* minmax multi tid */ +DATA(insert ( 4128 27 27 1 s 2799 3580 0 )); +DATA(insert ( 4128 27 27 2 s 2801 3580 0 )); +DATA(insert ( 4128 27 27 3 s 387 3580 0 )); +DATA(insert ( 4128 27 27 4 s 2802 3580 0 )); +DATA(insert ( 4128 27 27 5 s 2800 3580 0 )); /* minmax float (float4, float8) */ DATA(insert ( 4070 700 700 1 s 622 3580 0 )); DATA(insert ( 4070 700 700 2 s 624 3580 0 )); @@ -1028,6 +1086,27 @@ DATA(insert ( 4070 701 701 2 s 673 3580 0 )); DATA(insert ( 4070 701 701 3 s 670 3580 0 )); DATA(insert ( 4070 701 701 4 s 675 3580 0 )); DATA(insert ( 4070 701 701 5 s 674 3580 0 )); +/* minmax multi (float8) */ +DATA(insert ( 4005 700 700 1 s 622 3580 0 )); +DATA(insert ( 4005 700 700 2 s 624 3580 0 )); +DATA(insert ( 4005 700 700 3 s 620 3580 0 )); +DATA(insert ( 4005 700 700 4 s 625 3580 0 )); +DATA(insert ( 4005 700 700 5 s 623 3580 0 )); +DATA(insert ( 4005 700 701 1 s 1122 3580 0 )); +DATA(insert ( 4005 700 701 2 s 1124 3580 0 )); +DATA(insert ( 4005 700 701 3 s 1120 3580 0 )); +DATA(insert ( 4005 700 701 4 s 1125 3580 0 )); +DATA(insert ( 4005 700 701 5 s 1123 3580 0 )); +DATA(insert ( 4005 701 700 1 s 1132 3580 0 )); +DATA(insert ( 4005 701 700 2 s 1134 3580 0 )); +DATA(insert ( 4005 701 700 3 s 1130 3580 0 )); +DATA(insert ( 4005 701 700 4 s 1135 3580 0 )); +DATA(insert ( 4005 701 700 5 s 1133 3580 0 )); +DATA(insert ( 4005 701 701 1 s 672 3580 0 )); +DATA(insert ( 4005 701 701 2 s 673 3580 0 )); +DATA(insert ( 4005 701 701 3 s 670 3580 0 )); +DATA(insert ( 4005 701 701 4 s 675 3580 0 )); +DATA(insert ( 4005 701 701 5 s 674 3580 0 )); /* bloom float (float4, float8) */ DATA(insert ( 5030 700 700 1 s 620 3580 0 )); DATA(insert ( 5030 700 701 1 s 1120 3580 0 )); @@ -1040,6 +1119,12 @@ DATA(insert ( 4072 702 702 2 s 564 3580 0 )); DATA(insert ( 4072 702 702 3 s 560 3580 0 )); DATA(insert ( 4072 702 702 4 s 565 3580 0 )); DATA(insert ( 4072 702 702 5 s 563 3580 0 )); +/* multi minmax abstime */ +DATA(insert ( 4146 702 702 1 s 562 3580 0 )); +DATA(insert ( 4146 702 702 2 s 564 3580 0 )); +DATA(insert ( 4146 702 702 3 s 560 3580 0 )); +DATA(insert ( 4146 702 702 4 s 565 3580 0 )); +DATA(insert ( 4146 702 702 5 s 563 3580 0 )); /* bloom abstime */ DATA(insert ( 5031 702 702 1 s 560 3580 0 )); /* minmax reltime */ @@ -1048,6 +1133,12 @@ DATA(insert ( 4073 703 703 2 s 570 3580 0 )); DATA(insert ( 4073 703 703 3 s 566 3580 0 )); DATA(insert ( 4073 703 703 4 s 571 3580 0 )); DATA(insert ( 4073 703 703 5 s 569 3580 0 )); +/* multi minmax reltime */ +DATA(insert ( 4147 703 703 1 s 568 3580 0 )); +DATA(insert ( 4147 703 703 2 s 570 3580 0 )); +DATA(insert ( 4147 703 703 3 s 566 3580 0 )); +DATA(insert ( 4147 703 703 4 s 571 3580 0 )); +DATA(insert ( 4147 703 703 5 s 569 3580 0 )); /* bloom reltime */ DATA(insert ( 5032 703 703 1 s 566 3580 0 )); /* minmax macaddr */ @@ -1056,6 +1147,12 @@ DATA(insert ( 4074 829 829 2 s 1223 3580 0 )); DATA(insert ( 4074 829 829 3 s 1220 3580 0 )); DATA(insert ( 4074 829 829 4 s 1225 3580 0 )); DATA(insert ( 4074 829 829 5 s 1224 3580 0 )); +/* multi minmax macaddr */ +DATA(insert ( 4143 829 829 1 s 1222 3580 0 )); +DATA(insert ( 4143 829 829 2 s 1223 3580 0 )); +DATA(insert ( 4143 829 829 3 s 1220 3580 0 )); +DATA(insert ( 4143 829 829 4 s 1225 3580 0 )); +DATA(insert ( 4143 829 829 5 s 1224 3580 0 )); /* bloom macaddr */ DATA(insert ( 5033 829 829 1 s 1220 3580 0 )); /* minmax macaddr8 */ @@ -1064,6 +1161,12 @@ DATA(insert ( 4109 774 774 2 s 3365 3580 0 )); DATA(insert ( 4109 774 774 3 s 3362 3580 0 )); DATA(insert ( 4109 774 774 4 s 3367 3580 0 )); DATA(insert ( 4109 774 774 5 s 3366 3580 0 )); +/* multi minmax macaddr8 */ +DATA(insert ( 4144 774 774 1 s 3364 3580 0 )); +DATA(insert ( 4144 774 774 2 s 3365 3580 0 )); +DATA(insert ( 4144 774 774 3 s 3362 3580 0 )); +DATA(insert ( 4144 774 774 4 s 3367 3580 0 )); +DATA(insert ( 4144 774 774 5 s 3366 3580 0 )); /* bloom macaddr8 */ DATA(insert ( 5034 774 774 1 s 3362 3580 0 )); /* minmax inet */ @@ -1072,6 +1175,12 @@ DATA(insert ( 4075 869 869 2 s 1204 3580 0 )); DATA(insert ( 4075 869 869 3 s 1201 3580 0 )); DATA(insert ( 4075 869 869 4 s 1206 3580 0 )); DATA(insert ( 4075 869 869 5 s 1205 3580 0 )); +/* multi minmax inet */ +DATA(insert ( 4145 869 869 1 s 1203 3580 0 )); +DATA(insert ( 4145 869 869 2 s 1204 3580 0 )); +DATA(insert ( 4145 869 869 3 s 1201 3580 0 )); +DATA(insert ( 4145 869 869 4 s 1206 3580 0 )); +DATA(insert ( 4145 869 869 5 s 1205 3580 0 )); /* inclusion inet */ DATA(insert ( 4102 869 869 3 s 3552 3580 0 )); DATA(insert ( 4102 869 869 7 s 934 3580 0 )); @@ -1095,6 +1204,12 @@ DATA(insert ( 4077 1083 1083 2 s 1111 3580 0 )); DATA(insert ( 4077 1083 1083 3 s 1108 3580 0 )); DATA(insert ( 4077 1083 1083 4 s 1113 3580 0 )); DATA(insert ( 4077 1083 1083 5 s 1112 3580 0 )); +/* multi minmax time without time zone */ +DATA(insert ( 4135 1083 1083 1 s 1110 3580 0 )); +DATA(insert ( 4135 1083 1083 2 s 1111 3580 0 )); +DATA(insert ( 4135 1083 1083 3 s 1108 3580 0 )); +DATA(insert ( 4135 1083 1083 4 s 1113 3580 0 )); +DATA(insert ( 4135 1083 1083 5 s 1112 3580 0 )); /* bloom time without time zone */ DATA(insert ( 5037 1083 1083 1 s 1108 3580 0 )); /* minmax datetime (date, timestamp, timestamptz) */ @@ -1143,6 +1258,52 @@ DATA(insert ( 4059 1184 1184 2 s 1323 3580 0 )); DATA(insert ( 4059 1184 1184 3 s 1320 3580 0 )); DATA(insert ( 4059 1184 1184 4 s 1325 3580 0 )); DATA(insert ( 4059 1184 1184 5 s 1324 3580 0 )); +/* minmax multi (timestamp, timestamptz) */ +DATA(insert ( 4006 1114 1114 1 s 2062 3580 0 )); +DATA(insert ( 4006 1114 1114 2 s 2063 3580 0 )); +DATA(insert ( 4006 1114 1114 3 s 2060 3580 0 )); +DATA(insert ( 4006 1114 1114 4 s 2065 3580 0 )); +DATA(insert ( 4006 1114 1114 5 s 2064 3580 0 )); +DATA(insert ( 4006 1114 1082 1 s 2371 3580 0 )); +DATA(insert ( 4006 1114 1082 2 s 2372 3580 0 )); +DATA(insert ( 4006 1114 1082 3 s 2373 3580 0 )); +DATA(insert ( 4006 1114 1082 4 s 2374 3580 0 )); +DATA(insert ( 4006 1114 1082 5 s 2375 3580 0 )); +DATA(insert ( 4006 1114 1184 1 s 2534 3580 0 )); +DATA(insert ( 4006 1114 1184 2 s 2535 3580 0 )); +DATA(insert ( 4006 1114 1184 3 s 2536 3580 0 )); +DATA(insert ( 4006 1114 1184 4 s 2537 3580 0 )); +DATA(insert ( 4006 1114 1184 5 s 2538 3580 0 )); +DATA(insert ( 4006 1082 1082 1 s 1095 3580 0 )); +DATA(insert ( 4006 1082 1082 2 s 1096 3580 0 )); +DATA(insert ( 4006 1082 1082 3 s 1093 3580 0 )); +DATA(insert ( 4006 1082 1082 4 s 1098 3580 0 )); +DATA(insert ( 4006 1082 1082 5 s 1097 3580 0 )); +DATA(insert ( 4006 1082 1114 1 s 2345 3580 0 )); +DATA(insert ( 4006 1082 1114 2 s 2346 3580 0 )); +DATA(insert ( 4006 1082 1114 3 s 2347 3580 0 )); +DATA(insert ( 4006 1082 1114 4 s 2348 3580 0 )); +DATA(insert ( 4006 1082 1114 5 s 2349 3580 0 )); +DATA(insert ( 4006 1082 1184 1 s 2358 3580 0 )); +DATA(insert ( 4006 1082 1184 2 s 2359 3580 0 )); +DATA(insert ( 4006 1082 1184 3 s 2360 3580 0 )); +DATA(insert ( 4006 1082 1184 4 s 2361 3580 0 )); +DATA(insert ( 4006 1082 1184 5 s 2362 3580 0 )); +DATA(insert ( 4006 1184 1082 1 s 2384 3580 0 )); +DATA(insert ( 4006 1184 1082 2 s 2385 3580 0 )); +DATA(insert ( 4006 1184 1082 3 s 2386 3580 0 )); +DATA(insert ( 4006 1184 1082 4 s 2387 3580 0 )); +DATA(insert ( 4006 1184 1082 5 s 2388 3580 0 )); +DATA(insert ( 4006 1184 1114 1 s 2540 3580 0 )); +DATA(insert ( 4006 1184 1114 2 s 2541 3580 0 )); +DATA(insert ( 4006 1184 1114 3 s 2542 3580 0 )); +DATA(insert ( 4006 1184 1114 4 s 2543 3580 0 )); +DATA(insert ( 4006 1184 1114 5 s 2544 3580 0 )); +DATA(insert ( 4006 1184 1184 1 s 1322 3580 0 )); +DATA(insert ( 4006 1184 1184 2 s 1323 3580 0 )); +DATA(insert ( 4006 1184 1184 3 s 1320 3580 0 )); +DATA(insert ( 4006 1184 1184 4 s 1325 3580 0 )); +DATA(insert ( 4006 1184 1184 5 s 1324 3580 0 )); /* bloom datetime (date, timestamp, timestamptz) */ DATA(insert ( 5038 1114 1114 1 s 2060 3580 0 )); DATA(insert ( 5038 1114 1082 1 s 2373 3580 0 )); @@ -1160,6 +1321,12 @@ DATA(insert ( 4078 1186 1186 2 s 1333 3580 0 )); DATA(insert ( 4078 1186 1186 3 s 1330 3580 0 )); DATA(insert ( 4078 1186 1186 4 s 1335 3580 0 )); DATA(insert ( 4078 1186 1186 5 s 1334 3580 0 )); +/* multi minmax interval */ +DATA(insert ( 4134 1186 1186 1 s 1332 3580 0 )); +DATA(insert ( 4134 1186 1186 2 s 1333 3580 0 )); +DATA(insert ( 4134 1186 1186 3 s 1330 3580 0 )); +DATA(insert ( 4134 1186 1186 4 s 1335 3580 0 )); +DATA(insert ( 4134 1186 1186 5 s 1334 3580 0 )); /* bloom interval */ DATA(insert ( 5041 1186 1186 1 s 1330 3580 0 )); /* minmax time with time zone */ @@ -1168,6 +1335,12 @@ DATA(insert ( 4058 1266 1266 2 s 1553 3580 0 )); DATA(insert ( 4058 1266 1266 3 s 1550 3580 0 )); DATA(insert ( 4058 1266 1266 4 s 1555 3580 0 )); DATA(insert ( 4058 1266 1266 5 s 1554 3580 0 )); +/* multi minmax time with time zone */ +DATA(insert ( 4133 1266 1266 1 s 1552 3580 0 )); +DATA(insert ( 4133 1266 1266 2 s 1553 3580 0 )); +DATA(insert ( 4133 1266 1266 3 s 1550 3580 0 )); +DATA(insert ( 4133 1266 1266 4 s 1555 3580 0 )); +DATA(insert ( 4133 1266 1266 5 s 1554 3580 0 )); /* bloom time with time zone */ DATA(insert ( 5042 1266 1266 1 s 1550 3580 0 )); /* minmax bit */ @@ -1188,6 +1361,12 @@ DATA(insert ( 4055 1700 1700 2 s 1755 3580 0 )); DATA(insert ( 4055 1700 1700 3 s 1752 3580 0 )); DATA(insert ( 4055 1700 1700 4 s 1757 3580 0 )); DATA(insert ( 4055 1700 1700 5 s 1756 3580 0 )); +/* minmax multi numeric */ +DATA(insert ( 4008 1700 1700 1 s 1754 3580 0 )); +DATA(insert ( 4008 1700 1700 2 s 1755 3580 0 )); +DATA(insert ( 4008 1700 1700 3 s 1752 3580 0 )); +DATA(insert ( 4008 1700 1700 4 s 1757 3580 0 )); +DATA(insert ( 4008 1700 1700 5 s 1756 3580 0 )); /* bloom numeric */ DATA(insert ( 5045 1700 1700 1 s 1752 3580 0 )); /* minmax uuid */ @@ -1196,6 +1375,12 @@ DATA(insert ( 4081 2950 2950 2 s 2976 3580 0 )); DATA(insert ( 4081 2950 2950 3 s 2972 3580 0 )); DATA(insert ( 4081 2950 2950 4 s 2977 3580 0 )); DATA(insert ( 4081 2950 2950 5 s 2975 3580 0 )); +/* minmax multi uuid */ +DATA(insert ( 4131 2950 2950 1 s 2974 3580 0 )); +DATA(insert ( 4131 2950 2950 2 s 2976 3580 0 )); +DATA(insert ( 4131 2950 2950 3 s 2972 3580 0 )); +DATA(insert ( 4131 2950 2950 4 s 2977 3580 0 )); +DATA(insert ( 4131 2950 2950 5 s 2975 3580 0 )); /* bloom uuid */ DATA(insert ( 5046 2950 2950 1 s 2972 3580 0 )); /* inclusion range types */ @@ -1219,6 +1404,12 @@ DATA(insert ( 4082 3220 3220 2 s 3226 3580 0 )); DATA(insert ( 4082 3220 3220 3 s 3222 3580 0 )); DATA(insert ( 4082 3220 3220 4 s 3227 3580 0 )); DATA(insert ( 4082 3220 3220 5 s 3225 3580 0 )); +/* multi minmax pg_lsn */ +DATA(insert ( 4132 3220 3220 1 s 3224 3580 0 )); +DATA(insert ( 4132 3220 3220 2 s 3226 3580 0 )); +DATA(insert ( 4132 3220 3220 3 s 3222 3580 0 )); +DATA(insert ( 4132 3220 3220 4 s 3227 3580 0 )); +DATA(insert ( 4132 3220 3220 5 s 3225 3580 0 )); /* bloom pg_lsn */ DATA(insert ( 5047 3220 3220 1 s 3222 3580 0 )); /* inclusion box */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 4871836..8fb3405 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -415,6 +415,55 @@ DATA(insert ( 4054 23 21 2 3384 )); DATA(insert ( 4054 23 21 3 3385 )); DATA(insert ( 4054 23 21 4 3386 )); +/* minmax multi integer: int2, int4, int8 */ +DATA(insert ( 4126 20 20 1 4001 )); +DATA(insert ( 4126 20 20 2 4002 )); +DATA(insert ( 4126 20 20 3 4003 )); +DATA(insert ( 4126 20 20 4 4004 )); +DATA(insert ( 4126 20 20 11 4013 )); +DATA(insert ( 4126 20 21 1 4001 )); +DATA(insert ( 4126 20 21 2 4002 )); +DATA(insert ( 4126 20 21 3 4003 )); +DATA(insert ( 4126 20 21 4 4004 )); +DATA(insert ( 4126 20 21 11 4013 )); +DATA(insert ( 4126 20 23 1 4001 )); +DATA(insert ( 4126 20 23 2 4002 )); +DATA(insert ( 4126 20 23 3 4003 )); +DATA(insert ( 4126 20 23 4 4004 )); +DATA(insert ( 4126 20 23 11 4013 )); + +DATA(insert ( 4126 21 21 1 4001 )); +DATA(insert ( 4126 21 21 2 4002 )); +DATA(insert ( 4126 21 21 3 4003 )); +DATA(insert ( 4126 21 21 4 4004 )); +DATA(insert ( 4126 21 21 11 4011 )); +DATA(insert ( 4126 21 20 1 4001 )); +DATA(insert ( 4126 21 20 2 4002 )); +DATA(insert ( 4126 21 20 3 4003 )); +DATA(insert ( 4126 21 20 4 4004 )); +DATA(insert ( 4126 21 20 11 4011 )); +DATA(insert ( 4126 21 23 1 4001 )); +DATA(insert ( 4126 21 23 2 4002 )); +DATA(insert ( 4126 21 23 3 4003 )); +DATA(insert ( 4126 21 23 4 4004 )); +DATA(insert ( 4126 21 23 11 4011 )); + +DATA(insert ( 4126 23 23 1 4001 )); +DATA(insert ( 4126 23 23 2 4002 )); +DATA(insert ( 4126 23 23 3 4003 )); +DATA(insert ( 4126 23 23 4 4004 )); +DATA(insert ( 4126 23 23 11 4012 )); +DATA(insert ( 4126 23 20 1 4001 )); +DATA(insert ( 4126 23 20 2 4002 )); +DATA(insert ( 4126 23 20 3 4003 )); +DATA(insert ( 4126 23 20 4 4004 )); +DATA(insert ( 4126 23 20 11 4012 )); +DATA(insert ( 4126 23 21 1 4001 )); +DATA(insert ( 4126 23 21 2 4002 )); +DATA(insert ( 4126 23 21 3 4003 )); +DATA(insert ( 4126 23 21 4 4004 )); +DATA(insert ( 4126 23 21 11 4012 )); + /* bloom integer: int2, int4, int8 */ DATA(insert ( 5024 20 20 1 5017 )); DATA(insert ( 5024 20 20 2 5018 )); @@ -450,6 +499,12 @@ DATA(insert ( 4068 26 26 1 3383 )); DATA(insert ( 4068 26 26 2 3384 )); DATA(insert ( 4068 26 26 3 3385 )); DATA(insert ( 4068 26 26 4 3386 )); +/* minmax multi oid */ +DATA(insert ( 4127 26 26 1 4001 )); +DATA(insert ( 4127 26 26 2 4002 )); +DATA(insert ( 4127 26 26 3 4003 )); +DATA(insert ( 4127 26 26 4 4004 )); +DATA(insert ( 4127 26 26 11 4012 )); /* bloom oid */ DATA(insert ( 5029 26 26 1 5017 )); DATA(insert ( 5029 26 26 2 5018 )); @@ -461,6 +516,12 @@ DATA(insert ( 4069 27 27 1 3383 )); DATA(insert ( 4069 27 27 2 3384 )); DATA(insert ( 4069 27 27 3 3385 )); DATA(insert ( 4069 27 27 4 3386 )); +/* minmax multi tid */ +DATA(insert ( 4128 27 27 1 4001 )); +DATA(insert ( 4128 27 27 2 4002 )); +DATA(insert ( 4128 27 27 3 4003 )); +DATA(insert ( 4128 27 27 4 4004 )); +DATA(insert ( 4128 27 27 11 4129 )); /* minmax float */ DATA(insert ( 4070 700 700 1 3383 )); DATA(insert ( 4070 700 700 2 3384 )); @@ -482,6 +543,31 @@ DATA(insert ( 4070 701 700 2 3384 )); DATA(insert ( 4070 701 700 3 3385 )); DATA(insert ( 4070 701 700 4 3386 )); +/* minmax multi float */ +DATA(insert ( 4005 700 700 1 4001 )); +DATA(insert ( 4005 700 700 2 4002 )); +DATA(insert ( 4005 700 700 3 4003 )); +DATA(insert ( 4005 700 700 4 4004 )); +DATA(insert ( 4005 700 700 11 4010 )); + +DATA(insert ( 4005 700 701 1 4001 )); +DATA(insert ( 4005 700 701 2 4002 )); +DATA(insert ( 4005 700 701 3 4003 )); +DATA(insert ( 4005 700 701 4 4004 )); +DATA(insert ( 4005 700 701 11 4010 )); + +DATA(insert ( 4005 701 701 1 4001 )); +DATA(insert ( 4005 701 701 2 4002 )); +DATA(insert ( 4005 701 701 3 4003 )); +DATA(insert ( 4005 701 701 4 4004 )); +DATA(insert ( 4005 701 701 11 4007 )); + +DATA(insert ( 4005 701 700 1 4001 )); +DATA(insert ( 4005 701 700 2 4002 )); +DATA(insert ( 4005 701 700 3 4003 )); +DATA(insert ( 4005 701 700 4 4004 )); +DATA(insert ( 4005 701 700 11 4007 )); + /* bloom float */ DATA(insert ( 5030 700 700 1 5017 )); DATA(insert ( 5030 700 700 2 5018 )); @@ -500,6 +586,12 @@ DATA(insert ( 4072 702 702 1 3383 )); DATA(insert ( 4072 702 702 2 3384 )); DATA(insert ( 4072 702 702 3 3385 )); DATA(insert ( 4072 702 702 4 3386 )); +/* multi minmax abstime */ +DATA(insert ( 4146 702 702 1 4001 )); +DATA(insert ( 4146 702 702 2 4002 )); +DATA(insert ( 4146 702 702 3 4003 )); +DATA(insert ( 4146 702 702 4 4004 )); +DATA(insert ( 4146 702 702 11 4148 )); /* bloom abstime */ DATA(insert ( 5031 702 702 1 5017 )); DATA(insert ( 5031 702 702 2 5018 )); @@ -511,6 +603,12 @@ DATA(insert ( 4073 703 703 1 3383 )); DATA(insert ( 4073 703 703 2 3384 )); DATA(insert ( 4073 703 703 3 3385 )); DATA(insert ( 4073 703 703 4 3386 )); +/* multi minmax reltime */ +DATA(insert ( 4147 703 703 1 4001 )); +DATA(insert ( 4147 703 703 2 4002 )); +DATA(insert ( 4147 703 703 3 4003 )); +DATA(insert ( 4147 703 703 4 4004 )); +DATA(insert ( 4147 703 703 11 4149 )); /* bloom reltime */ DATA(insert ( 5032 703 703 1 5017 )); DATA(insert ( 5032 703 703 2 5018 )); @@ -522,6 +620,12 @@ DATA(insert ( 4074 829 829 1 3383 )); DATA(insert ( 4074 829 829 2 3384 )); DATA(insert ( 4074 829 829 3 3385 )); DATA(insert ( 4074 829 829 4 3386 )); +/* multi minmax macaddr */ +DATA(insert ( 4143 829 829 1 4001 )); +DATA(insert ( 4143 829 829 2 4002 )); +DATA(insert ( 4143 829 829 3 4003 )); +DATA(insert ( 4143 829 829 4 4004 )); +DATA(insert ( 4143 829 829 11 4140 )); /* bloom macaddr */ DATA(insert ( 5033 829 829 1 5017 )); DATA(insert ( 5033 829 829 2 5018 )); @@ -533,6 +637,12 @@ DATA(insert ( 4109 774 774 1 3383 )); DATA(insert ( 4109 774 774 2 3384 )); DATA(insert ( 4109 774 774 3 3385 )); DATA(insert ( 4109 774 774 4 3386 )); +/* minmax macaddr8 */ +DATA(insert ( 4144 774 774 1 4001 )); +DATA(insert ( 4144 774 774 2 4002 )); +DATA(insert ( 4144 774 774 3 4003 )); +DATA(insert ( 4144 774 774 4 4004 )); +DATA(insert ( 4144 774 774 11 4141 )); /* bloom macaddr8 */ DATA(insert ( 5034 774 774 1 5017 )); DATA(insert ( 5034 774 774 2 5018 )); @@ -544,6 +654,12 @@ DATA(insert ( 4075 869 869 1 3383 )); DATA(insert ( 4075 869 869 2 3384 )); DATA(insert ( 4075 869 869 3 3385 )); DATA(insert ( 4075 869 869 4 3386 )); +/* multi minmax inet */ +DATA(insert ( 4145 869 869 1 4001 )); +DATA(insert ( 4145 869 869 2 4002 )); +DATA(insert ( 4145 869 869 3 4003 )); +DATA(insert ( 4145 869 869 4 4004 )); +DATA(insert ( 4145 869 869 11 4142 )); /* inclusion inet */ DATA(insert ( 4102 869 869 1 4105 )); DATA(insert ( 4102 869 869 2 4106 )); @@ -574,6 +690,12 @@ DATA(insert ( 4077 1083 1083 1 3383 )); DATA(insert ( 4077 1083 1083 2 3384 )); DATA(insert ( 4077 1083 1083 3 3385 )); DATA(insert ( 4077 1083 1083 4 3386 )); +/* multi minmax time without time zone */ +DATA(insert ( 4135 1083 1083 1 4001 )); +DATA(insert ( 4135 1083 1083 2 4002 )); +DATA(insert ( 4135 1083 1083 3 4003 )); +DATA(insert ( 4135 1083 1083 4 4004 )); +DATA(insert ( 4135 1083 1083 11 4136 )); /* bloom time without time zone */ DATA(insert ( 5037 1083 1083 1 5017 )); DATA(insert ( 5037 1083 1083 2 5018 )); @@ -620,6 +742,55 @@ DATA(insert ( 4059 1082 1184 2 3384 )); DATA(insert ( 4059 1082 1184 3 3385 )); DATA(insert ( 4059 1082 1184 4 3386 )); +/* minmax multi (date, timestamp, timestamptz) */ +DATA(insert ( 4006 1114 1114 1 4001 )); +DATA(insert ( 4006 1114 1114 2 4002 )); +DATA(insert ( 4006 1114 1114 3 4003 )); +DATA(insert ( 4006 1114 1114 4 4004 )); +DATA(insert ( 4006 1114 1114 11 4007 )); +DATA(insert ( 4006 1114 1184 1 4001 )); +DATA(insert ( 4006 1114 1184 2 4002 )); +DATA(insert ( 4006 1114 1184 3 4003 )); +DATA(insert ( 4006 1114 1184 4 4004 )); +DATA(insert ( 4006 1114 1184 11 4007 )); +DATA(insert ( 4006 1114 1082 1 4001 )); +DATA(insert ( 4006 1114 1082 2 4002 )); +DATA(insert ( 4006 1114 1082 3 4003 )); +DATA(insert ( 4006 1114 1082 4 4004 )); +DATA(insert ( 4006 1114 1082 11 4007 )); + +DATA(insert ( 4006 1184 1184 1 4001 )); +DATA(insert ( 4006 1184 1184 2 4002 )); +DATA(insert ( 4006 1184 1184 3 4003 )); +DATA(insert ( 4006 1184 1184 4 4004 )); +DATA(insert ( 4006 1184 1184 11 4007 )); +DATA(insert ( 4006 1184 1114 1 4001 )); +DATA(insert ( 4006 1184 1114 2 4002 )); +DATA(insert ( 4006 1184 1114 3 4003 )); +DATA(insert ( 4006 1184 1114 4 4004 )); +DATA(insert ( 4006 1184 1114 11 4007 )); +DATA(insert ( 4006 1184 1082 1 4001 )); +DATA(insert ( 4006 1184 1082 2 4002 )); +DATA(insert ( 4006 1184 1082 3 4003 )); +DATA(insert ( 4006 1184 1082 4 4004 )); +DATA(insert ( 4006 1184 1082 11 4007 )); + +DATA(insert ( 4006 1082 1082 1 4001 )); +DATA(insert ( 4006 1082 1082 2 4002 )); +DATA(insert ( 4006 1082 1082 3 4003 )); +DATA(insert ( 4006 1082 1082 4 4004 )); +DATA(insert ( 4006 1082 1082 11 4007 )); +DATA(insert ( 4006 1082 1114 1 4001 )); +DATA(insert ( 4006 1082 1114 2 4002 )); +DATA(insert ( 4006 1082 1114 3 4003 )); +DATA(insert ( 4006 1082 1114 4 4004 )); +DATA(insert ( 4006 1082 1114 11 4007 )); +DATA(insert ( 4006 1082 1184 1 4001 )); +DATA(insert ( 4006 1082 1184 2 4002 )); +DATA(insert ( 4006 1082 1184 3 4003 )); +DATA(insert ( 4006 1082 1184 4 4004 )); +DATA(insert ( 4006 1082 1184 11 4007 )); + /* bloom datetime (date, timestamp, timestamptz) */ DATA(insert ( 5038 1114 1114 1 5017 )); DATA(insert ( 5038 1114 1114 2 5018 )); @@ -644,6 +815,12 @@ DATA(insert ( 4078 1186 1186 1 3383 )); DATA(insert ( 4078 1186 1186 2 3384 )); DATA(insert ( 4078 1186 1186 3 3385 )); DATA(insert ( 4078 1186 1186 4 3386 )); +/* multi minmax interval */ +DATA(insert ( 4134 1186 1186 1 4001 )); +DATA(insert ( 4134 1186 1186 2 4002 )); +DATA(insert ( 4134 1186 1186 3 4003 )); +DATA(insert ( 4134 1186 1186 4 4004 )); +DATA(insert ( 4134 1186 1186 11 4137 )); /* bloom interval */ DATA(insert ( 5041 1186 1186 1 5017 )); DATA(insert ( 5041 1186 1186 2 5018 )); @@ -655,6 +832,12 @@ DATA(insert ( 4058 1266 1266 1 3383 )); DATA(insert ( 4058 1266 1266 2 3384 )); DATA(insert ( 4058 1266 1266 3 3385 )); DATA(insert ( 4058 1266 1266 4 3386 )); +/* multi minmax time with time zone */ +DATA(insert ( 4133 1266 1266 1 4001 )); +DATA(insert ( 4133 1266 1266 2 4002 )); +DATA(insert ( 4133 1266 1266 3 4003 )); +DATA(insert ( 4133 1266 1266 4 4004 )); +DATA(insert ( 4133 1266 1266 11 4138 )); /* bloom time with time zone */ DATA(insert ( 5042 1266 1266 1 5017 )); DATA(insert ( 5042 1266 1266 2 5018 )); @@ -676,6 +859,12 @@ DATA(insert ( 4055 1700 1700 1 3383 )); DATA(insert ( 4055 1700 1700 2 3384 )); DATA(insert ( 4055 1700 1700 3 3385 )); DATA(insert ( 4055 1700 1700 4 3386 )); +/* minmax multi numeric */ +DATA(insert ( 4008 1700 1700 1 4001 )); +DATA(insert ( 4008 1700 1700 2 4002 )); +DATA(insert ( 4008 1700 1700 3 4003 )); +DATA(insert ( 4008 1700 1700 4 4004 )); +DATA(insert ( 4008 1700 1700 11 4009 )); /* bloom numeric */ DATA(insert ( 5045 1700 1700 1 5017 )); DATA(insert ( 5045 1700 1700 2 5018 )); @@ -687,6 +876,12 @@ DATA(insert ( 4081 2950 2950 1 3383 )); DATA(insert ( 4081 2950 2950 2 3384 )); DATA(insert ( 4081 2950 2950 3 3385 )); DATA(insert ( 4081 2950 2950 4 3386 )); +/* minmax multi uuid */ +DATA(insert ( 4131 2950 2950 1 4001 )); +DATA(insert ( 4131 2950 2950 2 4002 )); +DATA(insert ( 4131 2950 2950 3 4003 )); +DATA(insert ( 4131 2950 2950 4 4004 )); +DATA(insert ( 4131 2950 2950 11 4130 )); /* bloom uuid */ DATA(insert ( 5046 2950 2950 1 5017 )); DATA(insert ( 5046 2950 2950 2 5018 )); @@ -706,6 +901,12 @@ DATA(insert ( 4082 3220 3220 1 3383 )); DATA(insert ( 4082 3220 3220 2 3384 )); DATA(insert ( 4082 3220 3220 3 3385 )); DATA(insert ( 4082 3220 3220 4 3386 )); +/* multi minmax pg_lsn */ +DATA(insert ( 4132 3220 3220 1 4001 )); +DATA(insert ( 4132 3220 3220 2 4002 )); +DATA(insert ( 4132 3220 3220 3 4003 )); +DATA(insert ( 4132 3220 3220 4 4004 )); +DATA(insert ( 4132 3220 3220 11 4139 )); /* bloom pg_lsn */ DATA(insert ( 5047 3220 3220 1 5017 )); DATA(insert ( 5047 3220 3220 2 5018 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 7dbd4ff..4e102e4 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -220,54 +220,75 @@ DATA(insert ( 3580 char_bloom_ops PGNSP PGUID 5022 18 f 18 )); DATA(insert ( 3580 name_minmax_ops PGNSP PGUID 4065 19 t 19 )); DATA(insert ( 3580 name_bloom_ops PGNSP PGUID 5023 19 f 19 )); DATA(insert ( 3580 int8_minmax_ops PGNSP PGUID 4054 20 t 20 )); +DATA(insert ( 3580 int8_minmax_multi_ops PGNSP PGUID 4126 20 f 20 )); DATA(insert ( 3580 int8_bloom_ops PGNSP PGUID 5024 20 f 20 )); DATA(insert ( 3580 int2_minmax_ops PGNSP PGUID 4054 21 t 21 )); +DATA(insert ( 3580 int2_minmax_multi_ops PGNSP PGUID 4126 21 f 21 )); DATA(insert ( 3580 int2_bloom_ops PGNSP PGUID 5024 21 f 21 )); DATA(insert ( 3580 int4_minmax_ops PGNSP PGUID 4054 23 t 23 )); +DATA(insert ( 3580 int4_minmax_multi_ops PGNSP PGUID 4126 23 f 23 )); DATA(insert ( 3580 int4_bloom_ops PGNSP PGUID 5024 23 f 23 )); DATA(insert ( 3580 text_minmax_ops PGNSP PGUID 4056 25 t 25 )); DATA(insert ( 3580 text_bloom_ops PGNSP PGUID 5027 25 f 25 )); DATA(insert ( 3580 oid_minmax_ops PGNSP PGUID 4068 26 t 26 )); +DATA(insert ( 3580 oid_minmax_multi_ops PGNSP PGUID 4127 26 f 26 )); DATA(insert ( 3580 oid_bloom_ops PGNSP PGUID 5029 26 f 26 )); DATA(insert ( 3580 tid_minmax_ops PGNSP PGUID 4069 27 t 27 )); +DATA(insert ( 3580 tid_minmax_multi_ops PGNSP PGUID 4128 27 f 27 )); DATA(insert ( 3580 float4_minmax_ops PGNSP PGUID 4070 700 t 700 )); +DATA(insert ( 3580 float4_minmax_multi_ops PGNSP PGUID 4005 700 f 700 )); DATA(insert ( 3580 float4_bloom_ops PGNSP PGUID 5030 700 f 700 )); DATA(insert ( 3580 float8_minmax_ops PGNSP PGUID 4070 701 t 701 )); +DATA(insert ( 3580 float8_minmax_multi_ops PGNSP PGUID 4005 701 f 701 )); DATA(insert ( 3580 float8_bloom_ops PGNSP PGUID 5030 701 f 701 )); DATA(insert ( 3580 abstime_minmax_ops PGNSP PGUID 4072 702 t 702 )); +DATA(insert ( 3580 abstime_minmax_multi_ops PGNSP PGUID 4146 702 f 702 )); DATA(insert ( 3580 abstime_bloom_ops PGNSP PGUID 5031 702 f 702 )); DATA(insert ( 3580 reltime_minmax_ops PGNSP PGUID 4073 703 t 703 )); +DATA(insert ( 3580 reltime_minmax_multi_ops PGNSP PGUID 4147 703 f 703 )); DATA(insert ( 3580 reltime_bloom_ops PGNSP PGUID 5032 703 f 703 )); DATA(insert ( 3580 macaddr_minmax_ops PGNSP PGUID 4074 829 t 829 )); +DATA(insert ( 3580 macaddr_minmax_multi_ops PGNSP PGUID 4143 829 f 829 )); DATA(insert ( 3580 macaddr_bloom_ops PGNSP PGUID 5033 829 f 829 )); DATA(insert ( 3580 macaddr8_minmax_ops PGNSP PGUID 4109 774 t 774 )); +DATA(insert ( 3580 macaddr8_minmax_multi_ops PGNSP PGUID 4144 774 f 774 )); DATA(insert ( 3580 macaddr8_bloom_ops PGNSP PGUID 5034 774 f 774 )); DATA(insert ( 3580 inet_minmax_ops PGNSP PGUID 4075 869 f 869 )); +DATA(insert ( 3580 inet_minmax_multi_ops PGNSP PGUID 4145 869 f 869 )); DATA(insert ( 3580 inet_inclusion_ops PGNSP PGUID 4102 869 t 869 )); DATA(insert ( 3580 inet_bloom_ops PGNSP PGUID 5035 869 f 869 )); DATA(insert ( 3580 bpchar_minmax_ops PGNSP PGUID 4076 1042 t 1042 )); DATA(insert ( 3580 bpchar_bloom_ops PGNSP PGUID 5036 1042 f 1042 )); DATA(insert ( 3580 time_minmax_ops PGNSP PGUID 4077 1083 t 1083 )); +DATA(insert ( 3580 time_minmax_multi_ops PGNSP PGUID 4135 1083 f 1083 )); DATA(insert ( 3580 time_bloom_ops PGNSP PGUID 5037 1083 f 1083 )); DATA(insert ( 3580 date_minmax_ops PGNSP PGUID 4059 1082 t 1082 )); +DATA(insert ( 3580 date_minmax_multi_ops PGNSP PGUID 4006 1082 f 1082 )); DATA(insert ( 3580 date_bloom_ops PGNSP PGUID 5038 1082 f 1082 )); DATA(insert ( 3580 timestamp_minmax_ops PGNSP PGUID 4059 1114 t 1114 )); +DATA(insert ( 3580 timestamp_minmax_multi_ops PGNSP PGUID 4006 1114 f 1114 )); DATA(insert ( 3580 timestamp_bloom_ops PGNSP PGUID 5038 1114 f 1114 )); DATA(insert ( 3580 timestamptz_minmax_ops PGNSP PGUID 4059 1184 t 1184 )); +DATA(insert ( 3580 timestamptz_minmax_multi_ops PGNSP PGUID 4006 1184 f 1184 )); DATA(insert ( 3580 timestamptz_bloom_ops PGNSP PGUID 5038 1184 f 1184 )); DATA(insert ( 3580 interval_minmax_ops PGNSP PGUID 4078 1186 t 1186 )); +DATA(insert ( 3580 interval_minmax_multi_ops PGNSP PGUID 4134 1186 f 1186 )); DATA(insert ( 3580 interval_bloom_ops PGNSP PGUID 5041 1186 f 1186 )); DATA(insert ( 3580 timetz_minmax_ops PGNSP PGUID 4058 1266 t 1266 )); +DATA(insert ( 3580 timetz_minmax_multi_ops PGNSP PGUID 4133 1266 f 1266 )); DATA(insert ( 3580 timetz_bloom_ops PGNSP PGUID 5042 1266 f 1266 )); DATA(insert ( 3580 bit_minmax_ops PGNSP PGUID 4079 1560 t 1560 )); DATA(insert ( 3580 varbit_minmax_ops PGNSP PGUID 4080 1562 t 1562 )); DATA(insert ( 3580 numeric_minmax_ops PGNSP PGUID 4055 1700 t 1700 )); +DATA(insert ( 3580 numeric_minmax_multi_ops PGNSP PGUID 4008 1700 f 1700 )); DATA(insert ( 3580 numeric_bloom_ops PGNSP PGUID 5045 1700 f 1700 )); /* no brin opclass for record, anyarray */ DATA(insert ( 3580 uuid_minmax_ops PGNSP PGUID 4081 2950 t 2950 )); +DATA(insert ( 3580 uuid_minmax_multi_ops PGNSP PGUID 4131 2950 f 2950 )); DATA(insert ( 3580 uuid_bloom_ops PGNSP PGUID 5046 2950 f 2950 )); DATA(insert ( 3580 range_inclusion_ops PGNSP PGUID 4103 3831 t 3831 )); DATA(insert ( 3580 pg_lsn_minmax_ops PGNSP PGUID 4082 3220 t 3220 )); +DATA(insert ( 3580 pg_lsn_minmax_multi_ops PGNSP PGUID 4132 3220 f 3220 )); DATA(insert ( 3580 pg_lsn_bloom_ops PGNSP PGUID 5047 3220 f 3220 )); /* no brin opclass for enum, tsvector, tsquery, jsonb */ DATA(insert ( 3580 box_inclusion_ops PGNSP PGUID 4104 603 t 603 )); diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index bc22ce3..f0ce10b 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -160,14 +160,18 @@ DATA(insert OID = 4036 ( 2742 jsonb_ops PGNSP PGUID )); DATA(insert OID = 4037 ( 2742 jsonb_path_ops PGNSP PGUID )); DATA(insert OID = 4054 ( 3580 integer_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4126 ( 3580 integer_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5024 ( 3580 integer_bloom_ops PGNSP PGUID )); DATA(insert OID = 4055 ( 3580 numeric_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4008 ( 3580 numeric_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5045 ( 3580 numeric_bloom_ops PGNSP PGUID )); DATA(insert OID = 4056 ( 3580 text_minmax_ops PGNSP PGUID )); DATA(insert OID = 5027 ( 3580 text_bloom_ops PGNSP PGUID )); DATA(insert OID = 4058 ( 3580 timetz_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4133 ( 3580 timetz_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5042 ( 3580 timetz_bloom_ops PGNSP PGUID )); DATA(insert OID = 4059 ( 3580 datetime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4006 ( 3580 datetime_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5038 ( 3580 datetime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4062 ( 3580 char_minmax_ops PGNSP PGUID )); DATA(insert OID = 5022 ( 3580 char_bloom_ops PGNSP PGUID )); @@ -176,33 +180,45 @@ DATA(insert OID = 5021 ( 3580 bytea_bloom_ops PGNSP PGUID )); DATA(insert OID = 4065 ( 3580 name_minmax_ops PGNSP PGUID )); DATA(insert OID = 5023 ( 3580 name_bloom_ops PGNSP PGUID )); DATA(insert OID = 4068 ( 3580 oid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4127 ( 3580 oid_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5029 ( 3580 oid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4069 ( 3580 tid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4128 ( 3580 tid_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 4070 ( 3580 float_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4005 ( 3580 float_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5030 ( 3580 float_bloom_ops PGNSP PGUID )); DATA(insert OID = 4072 ( 3580 abstime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4146 ( 3580 abstime_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5031 ( 3580 abstime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4073 ( 3580 reltime_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4147 ( 3580 reltime_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5032 ( 3580 reltime_bloom_ops PGNSP PGUID )); DATA(insert OID = 4074 ( 3580 macaddr_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4143 ( 3580 macaddr_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5033 ( 3580 macaddr_bloom_ops PGNSP PGUID )); DATA(insert OID = 4109 ( 3580 macaddr8_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4144 ( 3580 macaddr8_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5034 ( 3580 macaddr8_bloom_ops PGNSP PGUID )); DATA(insert OID = 4075 ( 3580 network_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4145 ( 3580 network_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 4102 ( 3580 network_inclusion_ops PGNSP PGUID )); DATA(insert OID = 5035 ( 3580 network_bloom_ops PGNSP PGUID )); DATA(insert OID = 4076 ( 3580 bpchar_minmax_ops PGNSP PGUID )); DATA(insert OID = 5036 ( 3580 bpchar_bloom_ops PGNSP PGUID )); DATA(insert OID = 4077 ( 3580 time_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4135 ( 3580 time_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5037 ( 3580 time_bloom_ops PGNSP PGUID )); DATA(insert OID = 4078 ( 3580 interval_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4134 ( 3580 interval_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5041 ( 3580 interval_bloom_ops PGNSP PGUID )); DATA(insert OID = 4079 ( 3580 bit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4080 ( 3580 varbit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4081 ( 3580 uuid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4131 ( 3580 uuid_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5046 ( 3580 uuid_bloom_ops PGNSP PGUID )); DATA(insert OID = 4103 ( 3580 range_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4132 ( 3580 pg_lsn_minmax_multi_ops PGNSP PGUID )); DATA(insert OID = 5047 ( 3580 pg_lsn_bloom_ops PGNSP PGUID )); DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 7154e5e..c78c49f 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4364,6 +4364,50 @@ DESCR("BRIN minmax support"); DATA(insert OID = 3386 ( brin_minmax_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_union _null_ _null_ _null_ )); DESCR("BRIN minmax support"); +/* BRIN minmax multi */ +DATA(insert OID = 4001 ( brin_minmax_multi_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_opcinfo _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4002 ( brin_minmax_multi_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_add_value _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4003 ( brin_minmax_multi_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_consistent _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4004 ( brin_minmax_multi_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_union _null_ _null_ _null_ )); +DESCR("BRIN minmax support"); +DATA(insert OID = 4011 ( brin_minmax_multi_distance_int2 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_int2 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between int2 values"); +DATA(insert OID = 4012 ( brin_minmax_multi_distance_int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_int4 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between int4 values"); +DATA(insert OID = 4013 ( brin_minmax_multi_distance_int8 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_int8 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between int8 values"); +DATA(insert OID = 4010 ( brin_minmax_multi_distance_float4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_float4 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between float4 values"); +DATA(insert OID = 4007 ( brin_minmax_multi_distance_float8 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_float8 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between float8 values"); +DATA(insert OID = 4009 ( brin_minmax_multi_distance_numeric PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_numeric _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between numeric values"); +DATA(insert OID = 4129 ( brin_minmax_multi_distance_tid PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_tid _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between tid values"); +DATA(insert OID = 4130 ( brin_minmax_multi_distance_uuid PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_uuid _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between uuid values"); +DATA(insert OID = 4136 ( brin_minmax_multi_distance_time PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_time _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between time values"); +DATA(insert OID = 4137 ( brin_minmax_multi_distance_interval PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_interval _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between interval values"); +DATA(insert OID = 4138 ( brin_minmax_multi_distance_timetz PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_timetz _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between timetz values"); +DATA(insert OID = 4139 ( brin_minmax_multi_distance_pg_lsn PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_pg_lsn _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between pg_lsn values"); +DATA(insert OID = 4140 ( brin_minmax_multi_distance_macaddr PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_macaddr _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between macaddr values"); +DATA(insert OID = 4141 ( brin_minmax_multi_distance_macaddr8 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_macaddr8 _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between macaddr8 values"); +DATA(insert OID = 4142 ( brin_minmax_multi_distance_inet PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_inet _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between inet values"); +DATA(insert OID = 4148 ( brin_minmax_multi_distance_abstime PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_abstime _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between abstime values"); +DATA(insert OID = 4149 ( brin_minmax_multi_distance_reltime PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_multi_distance_reltime _null_ _null_ _null_ )); +DESCR("BRIN minmax compute distance between reltime values"); + /* BRIN inclusion */ DATA(insert OID = 4105 ( brin_inclusion_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_opcinfo _null_ _null_ _null_ )); DESCR("BRIN inclusion support"); diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000..a337006 --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,408 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 149999d..2c635d5 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- # Another group of parallel tests # ---------- -test: brin brin_bloom gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password +test: brin brin_bloom brin_multi gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password # ---------- # Another group of parallel tests diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000..263396f --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,361 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.9.5
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-02-05 20:27                     ` Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Mark Dilger @ 2018-02-05 20:27 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers


> BTW while working on the regression tests, I've noticed that brin.sql
> fails to test a couple of minmax opclasses (e.g. abstime/reltime). Is
> that intentional or is that something we should fix eventually?

I believe abstime/reltime are deprecated.  Perhaps nobody wanted to
bother adding test coverage for deprecated classes?  There was another
thread that discussed removing these types.  The consensus seemed to
be in favor of removing them, though I have not seen a patch for that yet.

mark





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
@ 2018-02-05 20:59                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-02-05 20:59 UTC (permalink / raw)
  To: Mark Dilger <hornschnorter@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 02/05/2018 09:27 PM, Mark Dilger wrote:
> 
>> BTW while working on the regression tests, I've noticed that brin.sql
>> fails to test a couple of minmax opclasses (e.g. abstime/reltime). Is
>> that intentional or is that something we should fix eventually?
> 
> I believe abstime/reltime are deprecated.  Perhaps nobody wanted to
> bother adding test coverage for deprecated classes?  There was another
> thread that discussed removing these types.  The consensus seemed to
> be in favor of removing them, though I have not seen a patch for that yet.
> 

Yeah, that's what I've been wondering about too. There's also this
comment in nabstime.h:

/*
 * Although time_t generally is a long int on 64 bit systems, these two
 * types must be 4 bytes, because that's what pg_type.h assumes. They
 * should be yanked (long) before 2038 and be replaced by timestamp and
 * interval.
 */

But then why adding BRIN opclasses at all? And if adding them, why not
to test them? We all know how long deprecation takes, particularly for
data types.

For me the question is whether to bother with adding the multi-minmax
opclasses, of course.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-02-05 23:40                         ` Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tom Lane @ 2018-02-05 23:40 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
> Yeah, that's what I've been wondering about too. There's also this
> comment in nabstime.h:

> /*
>  * Although time_t generally is a long int on 64 bit systems, these two
>  * types must be 4 bytes, because that's what pg_type.h assumes. They
>  * should be yanked (long) before 2038 and be replaced by timestamp and
>  * interval.
>  */

> But then why adding BRIN opclasses at all? And if adding them, why not
> to test them? We all know how long deprecation takes, particularly for
> data types.

There was some pretty recent chatter about removing these types; IIRC
Andres was annoyed about their lack of overflow checks.

I would definitely vote against adding any BRIN support for these types,
or indeed doing any work on them at all other than removal.

			regards, tom lane




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
@ 2018-02-06 00:25                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-02-06 00:25 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 02/06/2018 12:40 AM, Tom Lane wrote:
> Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
>> Yeah, that's what I've been wondering about too. There's also this
>> comment in nabstime.h:
> 
>> /*
>>  * Although time_t generally is a long int on 64 bit systems, these two
>>  * types must be 4 bytes, because that's what pg_type.h assumes. They
>>  * should be yanked (long) before 2038 and be replaced by timestamp and
>>  * interval.
>>  */
> 
>> But then why adding BRIN opclasses at all? And if adding them, why not
>> to test them? We all know how long deprecation takes, particularly for
>> data types.
> 
> There was some pretty recent chatter about removing these types;
> IIRC Andres was annoyed about their lack of overflow checks.
> 
> I would definitely vote against adding any BRIN support for these
> types, or indeed doing any work on them at all other than removal.
> 

Works for me. Ripping out the two opclasses from the patch is trivial.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-02-25 00:30                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-02-25 00:30 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi,

Attached is an updated patch series, fixing duplicate OIDs and removing
opclasses for reltime/abstime data types, as discussed.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 2.9K) download

  0003-BRIN-bloom-indexes.patch.gz (application/gzip, 17.5K) download

  0004-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 27.6K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-03-02 03:55                               ` Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 177+ messages in thread

From: Andres Freund @ 2018-03-02 03:55 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi,

On 2018-02-25 01:30:47 +0100, Tomas Vondra wrote:
> Note: Currently, this only works with float8-based data types.
> Supporting additional data types is not a big issue, but will
> require extending the opclass with "subtract" operator (used to
> compute distance between values when merging ranges).

Based on Tom's past stances I'm a bit doubtful he'd be happy with such a
restriction.  Note that something similar-ish also has come up in
0a459cec96.

I kinda wonder if there's any way to not have two similar but not equal
types of logic here?

    That problem is
    resolved here by adding the ability for btree operator classes to provide
    an "in_range" support function that defines how to add or subtract the
    RANGE offset value.  Factoring it this way also allows the operator class
    to avoid overflow problems near the ends of the datatype's range, if it
    wishes to expend effort on that.  (In the committed patch, the integer
    opclasses handle that issue, but it did not seem worth the trouble to
    avoid overflow failures for datetime types.)


- Andres




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
@ 2018-03-02 04:08                                 ` Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-02 17:09                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tom Lane @ 2018-03-02 04:08 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Andres Freund <andres@anarazel.de> writes:
> On 2018-02-25 01:30:47 +0100, Tomas Vondra wrote:
>> Note: Currently, this only works with float8-based data types.
>> Supporting additional data types is not a big issue, but will
>> require extending the opclass with "subtract" operator (used to
>> compute distance between values when merging ranges).

> Based on Tom's past stances I'm a bit doubtful he'd be happy with such a
> restriction.  Note that something similar-ish also has come up in
> 0a459cec96.

> I kinda wonder if there's any way to not have two similar but not equal
> types of logic here?

Hm.  I wonder what the patch intends to do with subtraction overflow,
or infinities, or NaNs.  Just as with the RANGE patch, it does not
seem to me that failure is really an acceptable option.  Indexes are
supposed to be able to index whatever the column datatype can store.

			regards, tom lane




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
@ 2018-03-02 17:09                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2018-03-02 17:09 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 03/02/2018 05:08 AM, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
>> On 2018-02-25 01:30:47 +0100, Tomas Vondra wrote:
>>> Note: Currently, this only works with float8-based data types.
>>> Supporting additional data types is not a big issue, but will
>>> require extending the opclass with "subtract" operator (used to
>>> compute distance between values when merging ranges).
> 
>> Based on Tom's past stances I'm a bit doubtful he'd be happy with such a
>> restriction.  Note that something similar-ish also has come up in
>> 0a459cec96.
> 

That restriction was lifted quite a long time ago, so now both index
types support pretty much the same data types as the original BRIN (with
the reltime/abstime exception, discussed in this thread earlier).

>> I kinda wonder if there's any way to not have two similar but not
>> equal types of logic here?
> 
> Hm. I wonder what the patch intends to do with subtraction overflow, 
> or infinities, or NaNs. Just as with the RANGE patch, it does not 
> seem to me that failure is really an acceptable option. Indexes are 
> supposed to be able to index whatever the column datatype can store.
> 

I admit that's something I haven't thought about very much. I'll look
into that, of course, but the indexes are only using the deltas to pick
which ranges to merge, so I think in the worst case it may results in
sub-optimal index. But let me check what the RANGE patch did.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
@ 2018-03-04 00:14                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:05                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  1 sibling, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2018-03-04 00:14 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi,

Attached is a patch version fixing breakage due to pg_proc changes
commited in fd1a421fe661.

On 03/02/2018 05:08 AM, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
>> On 2018-02-25 01:30:47 +0100, Tomas Vondra wrote:
>>> Note: Currently, this only works with float8-based data types.
>>> Supporting additional data types is not a big issue, but will
>>> require extending the opclass with "subtract" operator (used to
>>> compute distance between values when merging ranges).
> 
>> Based on Tom's past stances I'm a bit doubtful he'd be happy with 
>> such a restriction. Note that something similar-ish also has come 
>> up in 0a459cec96.
> 
>> I kinda wonder if there's any way to not have two similar but not 
>> equal types of logic here?
> 

I don't think it's very similar to what 0a459cec96 is doing. It's true
both deal with ranges of values, but that's about it - I don't see how
this patch could reuse some bits from 0a459cec96.

To elaborate, 0a459cec96 only really needs to know "does this value fall
into this range" while this patch needs to compare ranges by length.
That is, given a bunch of ranges (summary of values for a section of a
table), it needs to decide which ranges to merge - and it picks the
ranges with the smallest gap.

So for example with ranges [1,10], [15,20], [30,200], [250,300] it would
merge [1,10] and [15,20] because the gap between them is only 5, which
is shorter than the other gaps. This is used when the summary for a
range of pages gets "full" (the patch only keeps up to 32 ranges or so).

Not sure how I could reuse 0a459cec96 to do this.

> Hm. I wonder what the patch intends to do with subtraction overflow, 
> or infinities, or NaNs. Just as with the RANGE patch, it does not 
> seem to me that failure is really an acceptable option. Indexes are 
> supposed to be able to index whatever the column datatype can store.
> 

I've been thinking about this after looking at 0a459cec96, and I don't
think this patch has the same issues. One reason is that just like the
original minmax opclass, it does not really mess with the data it
stores. It only does min/max on the values, and stores that, so if there
was NaN or Infinity, it will index NaN or Infinity.

The subtraction is used only to decide which ranges to merge first, and
if the subtraction returns Infinity/NaN the ranges will be considered
very distant and merged last. Which is pretty much the desired behavior,
because it means -Infinity, Infinity and NaN will be keps as individual
"points" as long as possible.

Perhaps there is some other danger/thinko here, that I don't see?


The one overflow issue I found in the patch is that the numeric
"distance" function does this:

    d = DirectFunctionCall2(numeric_sub, a2, a1);	/* a2 - a1 */

    PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d));

which can overflow, of course. But that is not fatal - the index may get
inefficient due to non-optimal merging of ranges, but it will still
return correct results. But I think this can be easily improved by
passing not only the two values, but also minimum and maximum, and use
that to normalize the values to [0,1].


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 2.9K) download

  0003-BRIN-bloom-indexes.patch.gz (application/gzip, 17.5K) download

  0004-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 27.6K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-03-21 00:07                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-03-21 00:07 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 03/04/2018 01:14 AM, Tomas Vondra wrote:
> ...
> 
> The one overflow issue I found in the patch is that the numeric
> "distance" function does this:
> 
>     d = DirectFunctionCall2(numeric_sub, a2, a1);	/* a2 - a1 */
> 
>     PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d));
> 
> which can overflow, of course. But that is not fatal - the index may get
> inefficient due to non-optimal merging of ranges, but it will still
> return correct results. But I think this can be easily improved by
> passing not only the two values, but also minimum and maximum, and use
> that to normalize the values to [0,1].
> 

Attached is an updated patch series, addressing this possible overflow
the way I proposed - by computing (a2 - a1) / (b2 - b1), which is
guaranteed to produce a value between 0 and 1.

The two new arguments are ignored for most "distance" functions, because
those can't overflow or underflow in double precision AFAICS.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-a-20180320.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20180320.patch.gz (application/gzip, 2.9K) download

  0003-BRIN-bloom-indexes-20180320.patch.gz (application/gzip, 17.6K) download

  0004-BRIN-multi-range-minmax-indexes-20180320.patch.gz (application/gzip, 27.9K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-04-03 19:50                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-02-23 22:23                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 3 replies; 177+ messages in thread

From: Tomas Vondra @ 2018-04-03 19:50 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi,

attached is updated and slightly improved version of the two BRIN
opclasses (bloom and multi-range minmax). Given the lack of reviews I
think it's likely to get bumped to 2018-09, which I guess is OK - it
surely needs more feedback regarding some decisions. So let me share
some thoughts about those, before I forget all of it, and some test
results showing the pros/cons of those indexes.


1) index parameters

The main improvement of this version is an introduction of a couple of
BRIN index parameters, next to pages_per_range and autosummarize.

a) n_distinct_per_range - used to size Bloom index
b) false_positive_rate - used to size Bloom index
c) values_per_range - number of values in the minmax-multi summary

Until now those parameters were pretty much hard-coded, this allows easy
customization depending on the data set. There are some basic rules to
to clamp the values (e.g. not to allow ndistinct to be less than 128 or
more than MaxHeapTuplesPerPage * page_per_range), but that's about it.
I'm sure we could devise more elaborate heuristics (e.g. when building
index on an existing table, we could inspect table statistics first),
but the patch does not do that.

One disadvantage is that those parameters are per-index. It's possible
to define multi-column BRIN index, possibly with different opclasses:

  CREATE INDEX ON t USING brin (a int4_bloom_ops,
                                b int8_bloom_ops,
                                c int4_minmax_multi_ops,
                                d int8_minmax_multi_ops)
  WITH (false_positive_rate = 0.01,
        n_distinct_per_range = 1024,
        values_per_range = 32);

in which case the parameters apply to all columns (with the relevant
opclass type). So for example false_positive_rate applies to both "a"
and "b".

This is somewhat unfortunate, but I don't think it's worth inventing
more complex solution. If you need to specify different parameters, you
can simply build separate indexes, and it's more practical anyway
because all the summaries must fit on the same index page which limits
the per-column space. So people are more likely to define single-column
bloom indexes anyway.

There's a room for improvement when it comes to validating the
parameters. For example, it's possible to specify parameters that would
produce bloom filters larger than 8kB, which may lead with over-sized
index rows later. For minmax-multi indexes this should be relatively
safe (maximum number of values is 256, which is low enough for all
fixed-length types). Of course, varlena columns can break it, but we
can't really validate those anyway.


2) test results

The attached spreadsheet shows results comparing these opclasses to
existing BRIN indexes, and also to BTREE/GIN. Clearly, the dataset were
picked to show advantages of those approaches, e.g. on data sets where
regular minmax fails to deliver any benefits.

Overall I think it looks nice - the indexes are larger than minmax
(expected, the summaries are larger), but still orders of magnitude
smaller than BTREE or even GIN. For bloom the build time is comparable
to minmax, for minmax-multi it's somewhat slower - again, I'm sure
there's room for improvements.

For query performance, it's clearly better than plain minmax (but well,
the datasets were constructed to demonstrate that, so no surprise here).

One interesting thing I haven't realized initially is the relationship
between false positive rate for Bloom indexes, and the fraction of table
scanned by a query on average. Essentially, a bloom index with 1% false
positive rate is expected to scan about 1% of table on average. That
pretty accurately determines the performance of bloom indexes.



regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-a-20180403.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20180403.patch.gz (application/gzip, 2.9K) download

  0003-BRIN-bloom-indexes-20180403.patch.gz (application/gzip, 20.0K) download

  0004-BRIN-multi-range-minmax-indexes-20180403.patch.gz (application/gzip, 29.4K) download

  brin-results.ods (application/vnd.oasis.opendocument.spreadsheet, 65.9K) download

minmax-multi-queries.png (image/png, 12.6K) download view image
bloom-queries.png (image/png, 14.9K) download view image
bloom.sql (application/sql, 15.2K) download minmax-multi.sql (application/sql, 24.9K) download ^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-06-24 02:01                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-06-24 02:01 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi,

Attached is rebased version of this BRIN patch series, fixing mostly the
breakage due to 372728b0 (aka initial-catalog-data format changes). As
2018-07 CF is meant for almost-ready patches, this is more a 2018-09
material. But perhaps someone would like to take a look - and I'd have
to fix it anyway ...

At the pgcon dev meeting I suggested that long-running patches should
have a "summary" post once in a while, so that reviewers don't have to
reread the whole thread and follow all the various discussions. So let
me start with this thread, although it's not a particularly long or
complex one, nor does it have a long discussion. But anyway ...


The patches introduce two new BRIN opclasses - minmax-multi and bloom.


minmax-multi
============

minmax-multi is a variant of the current minmax opclass that handles
cases where the plain minmax opclass degrades due to outlier values.

Imagine almost perfectly correlated data (say, timestamps in a log
table) - that works great with regular minmax indexes. But if you go and
delete a bunch of historical messages (for whatever reason), new rows
with new timestamps will be routed to the empty space and the minmax
indexes will degrade because the ranges will get much "wider" due to the
new values.

The minmax-multi indexes deal with that by maintaining not a single
minmax range, but several of them. That allows tracking the outlier
values separately, without constructing one wide minmax range.

Consider this artificial example:

    create table t (a bigint, b int);

    alter t set (fillfactor=95);

    insert into t select i + 1000*random(), i+1000*random()
     from generate_series(1,100000000) s(i);

    update t set a = 1, b = 1 where random() < 0.001;
    update t set a = 100000000, b = 100000000 where random() < 0.001;

Now if you create a regular minmax index, it's going to perform
terribly, because pretty much every minmax range is [1,100000000] thanks
to the update of 0.1% of rows.

    create index on t using brin (a);

    explain analyze select * from t
    where a between 1923300::int and 1923600::int;

                                  QUERY PLAN
  -----------------------------------------------------------------
   Bitmap Heap Scan on t  (cost=75.11..75884.45 rows=319 width=12)
                (actual time=948.906..101739.892 rows=308 loops=1)
     Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
     Rows Removed by Index Recheck: 99999692
     Heap Blocks: lossy=568182
     ->  Bitmap Index Scan on t_a_idx  (cost=0.00..75.03 rows=22587
          width=0) (actual time=89.357..89.357 rows=5681920 loops=1)
           Index Cond: ((a >= 1923300) AND (a <= 1923600))
   Planning Time: 2.161 ms
   Execution Time: 101740.776 ms
  (8 rows)

But with the minmax-multi opclass, this is not an issue:

    create index on t using brin (a int8_minmax_multi_ops);

                                  QUERY PLAN
  -------------------------------------------------------------------
   Bitmap Heap Scan on t  (cost=1067.11..76876.45 rows=319 width=12)
                       (actual time=38.906..49.763 rows=308 loops=1)
     Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
     Rows Removed by Index Recheck: 22220
     Heap Blocks: lossy=128
     ->  Bitmap Index Scan on t_a_idx  (cost=0.00..1067.03 rows=22587
               width=0) (actual time=28.069..28.069 rows=1280 loops=1)
           Index Cond: ((a >= 1923300) AND (a <= 1923600))
   Planning Time: 1.715 ms
   Execution Time: 50.866 ms
  (8 rows)

Which is clearly a big improvement.

Doing this required some changes to how BRIN evaluates conditions on
page ranges. With a single minmax range it was enough to evaluate them
one by one, but minmax-multi needs to see all of them at once (to match
them against the partial ranges).

Most of the complexity is in building the summary, particularly picking
which values (partial ranges) to merge. The max number of values in the
summary is specified as values_per_range index reloption, and by default
it's set to 64, so there can be either 64 points or 32 intervals or some
combination of those.

I've been thinking about some automated way to tune this (either
globally or for each page range independently), but so far I have not
been very successful. The challenge is that making good decisions
requires global information about values in the column (e.g. global
minimum and maximum). I think the reloption with 64 as a default is a
good enough solution for now.

Perhaps the stats from pg_statistic would be useful for improving this
in the future, but I'm not sure.


bloom
=====

As the name suggests, this opclass uses bloom filter for the summary.
Compared to the minmax-multi it's a bit more experimental idea, but I
believe the foundations are safe.

Using bloom filter means that the index can only support equalities, but
for many use cases that's an acceptable limitation - UUID, IP addresses,
... (various identifiers in general).

Of course, how to size the bloom filter? It's worth noting the false
positive rate of the filter is essentially the fraction of a table that
will be scanned every time.

Similarly to the minmax-multi, parameters for computing optimal filter
size are set as reloptions (false_positive_rate, n_distinct_per_range)
with some reasonable defaults (1% false positive rate and distinct
values 10% of maximum heap tuples in a page range).

Note: When building the filter, we don't compute the hashes from the
original values, but we first use the type-specific hash function (the
same we'd use for hash indexes or hash joins) and then use the hash a as
an input for the bloom filter. This generally works fine, but if "our"
hash function generates a lot of collisions, it increases false positive
ratio of the whole filter. I'm not aware of a case where this would be
an issue, though.

What further complicates sizing of the bloom filter is available space -
the whole bloom filter needs to fit onto an 8kB page, and "full" bloom
filters with about 1/2 the bits set are pretty non-compressible. So
there's maybe ~8000 bytes for the bitmap. So for columns with many
distinct values, it may be necessary to make the page range smaller, to
reduce the number of distinct values in it.

And of course it requires good ndistinct estimates, not just for the
column as a whole, but for a single page range (because that's what
matters for sizing the bloom filter). Which is not a particularly
reliable estimate, I'm afraid.

So reloptions seem like a sufficient solution, at least for now.


open questions
==============

* I suspect the definition of cross-type opclasses (int2 vs. int8) are
not entirely correct. That probably needs another look.

* The bloom filter now works in two modes - sorted (where in the sorted
mode it stores the hashes directly) and hashed (the usual bloom filter
behavior). The idea is that for ranges with significantly fewer distinct
values, we only store those to save space (instead of allocating the
whole bloom filter with mostly 0 bits).


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 3.0K) download

  0003-BRIN-bloom-indexes.patch.gz (application/gzip, 20.5K) download

  0004-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 29.6K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-06-24 21:39                                           ` Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Thomas Munro @ 2018-06-24 21:39 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On Sun, Jun 24, 2018 at 2:01 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> Attached is rebased version of this BRIN patch series, fixing mostly the
> breakage due to 372728b0 (aka initial-catalog-data format changes). As
> 2018-07 CF is meant for almost-ready patches, this is more a 2018-09
> material. But perhaps someone would like to take a look - and I'd have
> to fix it anyway ...

Hi Tomas,

FYI Windows doesn't like this:

  src/backend/access/brin/brin_bloom.c(146): warning C4013: 'round'
undefined; assuming extern returning int
[C:\projects\postgresql\postgres.vcxproj]

  brin_bloom.obj : error LNK2019: unresolved external symbol round
referenced in function bloom_init
[C:\projects\postgresql\postgres.vcxproj]

-- 
Thomas Munro
http://www.enterprisedb.com




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
@ 2018-06-24 22:31                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-25 00:14                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-06-24 22:31 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 06/24/2018 11:39 PM, Thomas Munro wrote:
> On Sun, Jun 24, 2018 at 2:01 PM, Tomas Vondra
> <tomas.vondra@2ndquadrant.com> wrote:
>> Attached is rebased version of this BRIN patch series, fixing mostly the
>> breakage due to 372728b0 (aka initial-catalog-data format changes). As
>> 2018-07 CF is meant for almost-ready patches, this is more a 2018-09
>> material. But perhaps someone would like to take a look - and I'd have
>> to fix it anyway ...
> 
> Hi Tomas,
> 
> FYI Windows doesn't like this:
> 
>   src/backend/access/brin/brin_bloom.c(146): warning C4013: 'round'
> undefined; assuming extern returning int
> [C:\projects\postgresql\postgres.vcxproj]
> 
>   brin_bloom.obj : error LNK2019: unresolved external symbol round
> referenced in function bloom_init
> [C:\projects\postgresql\postgres.vcxproj]
> 

Thanks, I've noticed the failure before, but was not sure what's the
exact cause. It seems there's still no 'round' on Windows, so I'll
probably fix that by using rint() instead, or something like that.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-06-25 00:14                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-10-02 02:49                                                 ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2018-06-25 00:14 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers



On 06/25/2018 12:31 AM, Tomas Vondra wrote:
> On 06/24/2018 11:39 PM, Thomas Munro wrote:
>> On Sun, Jun 24, 2018 at 2:01 PM, Tomas Vondra
>> <tomas.vondra@2ndquadrant.com> wrote:
>>> Attached is rebased version of this BRIN patch series, fixing mostly the
>>> breakage due to 372728b0 (aka initial-catalog-data format changes). As
>>> 2018-07 CF is meant for almost-ready patches, this is more a 2018-09
>>> material. But perhaps someone would like to take a look - and I'd have
>>> to fix it anyway ...
>>
>> Hi Tomas,
>>
>> FYI Windows doesn't like this:
>>
>>   src/backend/access/brin/brin_bloom.c(146): warning C4013: 'round'
>> undefined; assuming extern returning int
>> [C:\projects\postgresql\postgres.vcxproj]
>>
>>   brin_bloom.obj : error LNK2019: unresolved external symbol round
>> referenced in function bloom_init
>> [C:\projects\postgresql\postgres.vcxproj]
>>
> 
> Thanks, I've noticed the failure before, but was not sure what's the
> exact cause. It seems there's still no 'round' on Windows, so I'll
> probably fix that by using rint() instead, or something like that.
> 

OK, here is a version tweaked to use floor()/ceil() instead of round().
Let's see if the Windows machine likes that more.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-a-20180625.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20180625.patch.gz (application/gzip, 3.0K) download

  0003-BRIN-bloom-indexes-20180625.patch.gz (application/gzip, 20.6K) download

  0004-BRIN-multi-range-minmax-indexes-20180625.patch.gz (application/gzip, 29.6K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-25 00:14                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2018-10-02 02:49                                                 ` Michael Paquier <michael@paquier.xyz>
  2019-02-04 05:54                                                   ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  0 siblings, 1 reply; 177+ messages in thread

From: Michael Paquier @ 2018-10-02 02:49 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi Tomas,

On Mon, Jun 25, 2018 at 02:14:20AM +0200, Tomas Vondra wrote:
> OK, here is a version tweaked to use floor()/ceil() instead of round().
> Let's see if the Windows machine likes that more.

The latest patch set does not apply cleanly.  Could you rebase it?  I
have moved the patch to CF 2018-10 for now, waiting on author.
--
Michael

Attachments:

  signature.asc (application/pgp-signature, 832B) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-25 00:14                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-10-02 02:49                                                 ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
@ 2019-02-04 05:54                                                   ` Michael Paquier <michael@paquier.xyz>
  2019-02-04 10:05                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Michael Paquier @ 2019-02-04 05:54 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On Tue, Oct 02, 2018 at 11:49:05AM +0900, Michael Paquier wrote:
> The latest patch set does not apply cleanly.  Could you rebase it?  I
> have moved the patch to CF 2018-10 for now, waiting on author.

It's been some time since that request, so I am marking the patch as
returned with feedback. 
--
Michael

Attachments:

  signature.asc (application/pgp-signature, 832B) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-25 00:14                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-10-02 02:49                                                 ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2019-02-04 05:54                                                   ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
@ 2019-02-04 10:05                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-02-04 10:13                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2019-02-04 10:05 UTC (permalink / raw)
  To: Michael Paquier <michael@paquier.xyz>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On 2/4/19 6:54 AM, Michael Paquier wrote:
> On Tue, Oct 02, 2018 at 11:49:05AM +0900, Michael Paquier wrote:
>> The latest patch set does not apply cleanly.  Could you rebase it?  I
>> have moved the patch to CF 2018-10 for now, waiting on author.
> 
> It's been some time since that request, so I am marking the patch as
> returned with feedback. 

But that's not the most recent version of the patch. On 28/12 I've
submitted an updated / rebased patch.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 02:01                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-24 21:39                                           ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@enterprisedb.com>
  2018-06-24 22:31                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-06-25 00:14                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-10-02 02:49                                                 ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2019-02-04 05:54                                                   ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2019-02-04 10:05                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-02-04 10:13                                                       ` Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 0 replies; 177+ messages in thread

From: Alvaro Herrera @ 2019-02-04 10:13 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Thomas Munro <thomas.munro@enterprisedb.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On 2019-Feb-04, Tomas Vondra wrote:

> On 2/4/19 6:54 AM, Michael Paquier wrote:
> > On Tue, Oct 02, 2018 at 11:49:05AM +0900, Michael Paquier wrote:
> >> The latest patch set does not apply cleanly.  Could you rebase it?  I
> >> have moved the patch to CF 2018-10 for now, waiting on author.
> > 
> > It's been some time since that request, so I am marking the patch as
> > returned with feedback. 
> 
> But that's not the most recent version of the patch. On 28/12 I've
> submitted an updated / rebased patch.

Moved to next commitfest instead.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-02-23 22:23                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2 siblings, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2019-02-23 22:23 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; +Cc: Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Apparently cputube did not pick the last version of the patches I've
submitted in December (and I don't see the message in the thread in
archive either), so it's listed as broken.

So here we go again, hopefully this time everything will go through ...

regards

On 12/28/18 12:45 AM, Tomas Vondra wrote:
> Hi all,
> 
> Attached is an updated/rebased version of the patch series. There are no
> changes to behavior, but let me briefly summarize the current state:
> 
> 0001 and 0002
> -------------
> 
> The first two parts are "just" refactoring the existing code to pass all
> scankeys to the opclass at once - this is needed by the new minmax-like
> opclass, but per discussion with Alvaro it seems worthwhile even
> independently. I tend to agree with that. Similarly for the second part,
> which moves all IS NULL checks entirely to bringetbimap().
> 
> 0003 bloom opclass
> ------------------
> 
> The first new opclasss, based on bloom filters. For each page range
> (i.e. 1MB by default) a small bloom filter is built (with hash values of
> the original values as inputs), and then used to evaluate equality
> queries. A small optimization is that initially the actual (hash) values
> are kept until reaching the bloom filter size. This improves behavior in
> low-cardinality data sets.
> 
> Picking the bloom filter parameters is the tricky part - we don't have a
> reliable source of such information (namely number of distinct values
> per range), and e.g. the false positive rate actually has to be picked
> by the user because it's a compromise between index size and accuracy.
> Essentially, false positive rate is the fraction of the table that has
> to be scanned for a random value (on average). But it also makes the
> index larger, because the per-range bloom filters will be larger.
> 
> Another reason why this needs to be defined by the user is that the
> space for index tuple is limited by one page (8kB by default), so we
> can't allow the bloom filter to be larger (we have to assume it's
> non-compressible, because in the optimal fill it's 50% 0s and 1s). But
> the BRIN index may be multi-column, and the limit applies to the whole
> tuple. And we don't know what the opclasses or parameters of other
> columns are.
> 
> So the patch simply adds two reloptions
> 
> a) n_distinct_per_range - number of distinct values per range
> b) false_positive_rate - false positive rate of the filter
> 
> There are some simple heuristics to ensure the values are reasonable
> (e.g. upper limit for number of distinct values, etc.) and perhaps we
> might consider stats from the underlying table (when not empty), but the
> patch does not do that.
> 
> 
> 0004 multi-minmax opclass
> -------------------------
> 
> The second opclass addresses a common issue for minmax indexes, where
> the table is initially nicely correlated with the index, and it works
> fine. But then deletes/updates route data into other parts of the table
> making the ranges very wide ad rendering the BRIN index inefficient.
> 
> One way to deal improve this would be considering the index(es) while
> routing the new tuple, i.e. looking not only for page with enough free
> space, but for pages in already matching ranges (or close to it).
> 
> A partitioning is a possible approach so segregate the data. But it's
> certainly much higher overhead, both in terms of maintenance and
> planning (particularly with  1:1 of ranges vs. partitions).
> 
> So the new multi-minmax opclass takes a different approach, replacing
> the one minmax range with multiple ranges (64 boundary values or 32
> ranges by default). Initially individual values are stored, and after
> reaching the maximum number of values the values are merged into ranges
> by distance. This allows handling outliers very efficiently, because
> they will not be merged with the "main" range for as long as possible.
> 
> Similarly to the bloom opclass, the main challenge here is deciding the
> parameter - in this case, it's "number of values per range". Again, it's
> a compromise vs. index size and efficiency. The default (64 values) is
> fairly reasonable, but ultimately it's up to the user - there is a new
> reloption "values_per_range".
> 
> 
> 
> regards
> 

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

  0001-Pass-all-keys-to-BRIN-consistent-function-a-20190223.patch.gz (application/gzip, 5.4K) download

  0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20190223.patch.gz (application/gzip, 3.0K) download

  0003-BRIN-bloom-indexes-20190223.patch.gz (application/gzip, 20.6K) download

  0004-BRIN-multi-range-minmax-indexes-20190223.patch.gz (application/gzip, 29.6K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-03-02 09:00                                         ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2 siblings, 1 reply; 177+ messages in thread

From: Alexander Korotkov @ 2019-03-02 09:00 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

Hi!

I'm starting to look at this patchset.  In the general, I think it's
very cool!  We definitely need this.

On Tue, Apr 3, 2018 at 10:51 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> 1) index parameters
>
> The main improvement of this version is an introduction of a couple of
> BRIN index parameters, next to pages_per_range and autosummarize.
>
> a) n_distinct_per_range - used to size Bloom index
> b) false_positive_rate - used to size Bloom index
> c) values_per_range - number of values in the minmax-multi summary
>
> Until now those parameters were pretty much hard-coded, this allows easy
> customization depending on the data set. There are some basic rules to
> to clamp the values (e.g. not to allow ndistinct to be less than 128 or
> more than MaxHeapTuplesPerPage * page_per_range), but that's about it.
> I'm sure we could devise more elaborate heuristics (e.g. when building
> index on an existing table, we could inspect table statistics first),
> but the patch does not do that.
>
> One disadvantage is that those parameters are per-index.

For me, the main disadvantage of this solution is that we put
opclass-specific parameters into access method.  And this is generally
bad design.  So, user can specify such parameter if even not using
corresponding opclass, that may cause a confuse (if even we forbid
that, it needs to be hardcoded).  Also, extension opclasses can't do
the same thing.  Thus, it appears that extension opclasses are not
first class citizens anymore.  Have you take a look at opclass
parameters patch [1]?  I think it's proper solution of this problem.
I think we should postpone this parameterization until we push opclass
parameters patch.

1. https://www.postgresql.org/message-id/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2019-03-02 21:25                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2019-03-02 21:25 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers



On 3/2/19 10:00 AM, Alexander Korotkov wrote:
> Hi!
> 
> I'm starting to look at this patchset.  In the general, I think it's
> very cool!  We definitely need this.
> 
> On Tue, Apr 3, 2018 at 10:51 PM Tomas Vondra
> <tomas.vondra@2ndquadrant.com> wrote:
>> 1) index parameters
>>
>> The main improvement of this version is an introduction of a couple of
>> BRIN index parameters, next to pages_per_range and autosummarize.
>>
>> a) n_distinct_per_range - used to size Bloom index
>> b) false_positive_rate - used to size Bloom index
>> c) values_per_range - number of values in the minmax-multi summary
>>
>> Until now those parameters were pretty much hard-coded, this allows easy
>> customization depending on the data set. There are some basic rules to
>> to clamp the values (e.g. not to allow ndistinct to be less than 128 or
>> more than MaxHeapTuplesPerPage * page_per_range), but that's about it.
>> I'm sure we could devise more elaborate heuristics (e.g. when building
>> index on an existing table, we could inspect table statistics first),
>> but the patch does not do that.
>>
>> One disadvantage is that those parameters are per-index.
> 
> For me, the main disadvantage of this solution is that we put
> opclass-specific parameters into access method.  And this is generally
> bad design.  So, user can specify such parameter if even not using
> corresponding opclass, that may cause a confuse (if even we forbid
> that, it needs to be hardcoded).  Also, extension opclasses can't do
> the same thing.  Thus, it appears that extension opclasses are not
> first class citizens anymore.  Have you take a look at opclass
> parameters patch [1]?  I think it's proper solution of this problem.
> I think we should postpone this parameterization until we push opclass
> parameters patch.
> 
> 1. https://www.postgresql.org/message-id/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
> 

I've looked at that patch only very briefly so far, but I agree it's
likely a better solution than what my patch does at the moment (which I
agree is a misuse of the AM-level options). I'll take a closer look.

I agree it makes sense to re-use that infrastructure for this patch, but
I'm hesitant to rebase it on top of that patch right away. Because it
would mean this thread dependent on it, which would confuse cputube,
make it bitrot faster etc.

So I suggest we ignore this aspect of the patch for now, and let's talk
about the other bits first.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-03-03 04:29                                             ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alexander Korotkov @ 2019-03-03 04:29 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On Sun, Mar 3, 2019 at 12:25 AM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> I've looked at that patch only very briefly so far, but I agree it's
> likely a better solution than what my patch does at the moment (which I
> agree is a misuse of the AM-level options). I'll take a closer look.
>
> I agree it makes sense to re-use that infrastructure for this patch, but
> I'm hesitant to rebase it on top of that patch right away. Because it
> would mean this thread dependent on it, which would confuse cputube,
> make it bitrot faster etc.
>
> So I suggest we ignore this aspect of the patch for now, and let's talk
> about the other bits first.

Works for me.  We don't need to make the whole work made by this patch
to be dependent on opclass parameters.  It's OK to ignore this aspect
for now and come back when opclass parameters get committed.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company




^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2019-06-11 17:30                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-10 08:19                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2019-06-11 17:30 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers

On Sun, Mar 03, 2019 at 07:29:26AM +0300, Alexander Korotkov wrote:
>On Sun, Mar 3, 2019 at 12:25 AM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> I've looked at that patch only very briefly so far, but I agree it's
>> likely a better solution than what my patch does at the moment (which I
>> agree is a misuse of the AM-level options). I'll take a closer look.
>>
>> I agree it makes sense to re-use that infrastructure for this patch, but
>> I'm hesitant to rebase it on top of that patch right away. Because it
>> would mean this thread dependent on it, which would confuse cputube,
>> make it bitrot faster etc.
>>
>> So I suggest we ignore this aspect of the patch for now, and let's talk
>> about the other bits first.
>
>Works for me.  We don't need to make the whole work made by this patch
>to be dependent on opclass parameters.  It's OK to ignore this aspect
>for now and come back when opclass parameters get committed.
>

Attached is this patch series, rebased on top of current master and the
opclass parameters patch [1]. I previously planned to keep those two
efforts separate for a while, but I decided to give it a try and the
breakage is fairly minor so I'll keep it this way - this patch has zero
chance of getting committed with the opclass parameters patch anyway.

Aside from rebase and changes due to adopting opclass parameters, the
patch is otherwise unchanged.

0001-0004 are just the opclass parameters patch series.

0005 adds opclass parameters to BRIN indexes (similarly to what the
preceding parts to for GIN/GiST indexes).

0006-0010 are the original patch series (BRIN tweaks, bloom and
multi-minmax) rebased and switched to opclass parameters.


So now, we can do things like this:

  CREATE INDEX x ON t USING brin (
      col1 int4_bloom_ops(false_positive_rate = 0.05),
      col2 int4_minmax_multi_ops(values_per_range = 16)
  ) WITH (pages_per_range = 32);

and so on. I think the patch [1] works fine - I only have some minor
comments, that I'll post to that thread.

The other challenges (e.g. how to pick the values for opclass parameters
automatically, based on the data) are still open.


regards


[1] https://www.postgresql.org/message-id/flat/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru 

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (10 files, .tar.gz)

0001-Add-opclass-parameters-20190611.patch (text/plain, 41.7K) download From 436eca7a1dd45b78b50c3868fd4be518b814cecd Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 20:59:01 +0200 Subject: [PATCH 01/10] Add opclass parameters --- doc/src/sgml/indices.sgml | 2 +- doc/src/sgml/ref/create_index.sgml | 16 ++- src/backend/access/common/reloptions.c | 142 +++++++++++++++------- src/backend/access/index/indexam.c | 81 ++++++++++++ src/backend/catalog/heap.c | 8 +- src/backend/catalog/index.c | 23 +++- src/backend/catalog/toasting.c | 1 + src/backend/commands/indexcmds.c | 17 ++- src/backend/commands/tablecmds.c | 2 +- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/optimizer/util/plancat.c | 4 + src/backend/parser/gram.y | 72 +++++++---- src/backend/utils/adt/ruleutils.c | 128 +++++++++++-------- src/backend/utils/cache/relcache.c | 99 +++++++++++++++ src/include/access/amapi.h | 7 ++ src/include/access/genam.h | 5 + src/include/access/reloptions.h | 5 + src/include/catalog/heap.h | 1 + src/include/nodes/execnodes.h | 2 + src/include/nodes/parsenodes.h | 1 + src/include/nodes/pathnodes.h | 1 + src/include/utils/rel.h | 1 + src/include/utils/relcache.h | 3 + src/include/utils/ruleutils.h | 2 + src/test/regress/expected/btree_index.out | 5 + src/test/regress/sql/btree_index.sql | 4 + 28 files changed, 503 insertions(+), 132 deletions(-) diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml index 95c0a1926c..ea3acea88e 100644 --- a/doc/src/sgml/indices.sgml +++ b/doc/src/sgml/indices.sgml @@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success; An index definition can specify an <firstterm>operator class</firstterm> for each column of an index. <synopsis> -CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>); +CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>); </synopsis> The operator class identifies the operators to be used by the index for that column. For example, a B-tree index on the type <type>int4</type> diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 629a31ef79..61401f3645 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -22,7 +22,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ] - ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) + ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] @@ -278,6 +278,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><replaceable class="parameter">opclass_parameter</replaceable></term> + <listitem> + <para> + The name of an operator class parameter. See below for details. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>ASC</literal></term> <listitem> @@ -646,8 +655,9 @@ Indexes: </para> <para> - An <firstterm>operator class</firstterm> can be specified for each - column of an index. The operator class identifies the operators to be + An <firstterm>operator class</firstterm> with its optional parameters + can be specified for each column of an index. + The operator class identifies the operators to be used by the index for that column. For example, a B-tree index on four-byte integers would use the <literal>int4_ops</literal> class; this operator class includes comparison functions for four-byte diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index de06c92574..abc0082ce7 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -1051,6 +1051,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, return options; } +static void +parseRelOptionsInternal(Datum options, bool validate, + relopt_value *reloptions, int numoptions) +{ + ArrayType *array = DatumGetArrayTypeP(options); + Datum *optiondatums; + int noptions; + int i; + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &optiondatums, NULL, &noptions); + + for (i = 0; i < noptions; i++) + { + char *text_str = VARDATA(optiondatums[i]); + int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ; + int j; + + /* Search for a match in reloptions */ + for (j = 0; j < numoptions; j++) + { + int kw_len = reloptions[j].gen->namelen; + + if (text_len > kw_len && text_str[kw_len] == '=' && + strncmp(text_str, reloptions[j].gen->name, kw_len) == 0) + { + parse_one_reloption(&reloptions[j], text_str, text_len, + validate); + break; + } + } + + if (j >= numoptions && validate) + { + char *s; + char *p; + + s = TextDatumGetCString(optiondatums[i]); + p = strchr(s, '='); + if (p) + *p = '\0'; + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized parameter \"%s\"", s))); + } + } + + /* It's worth avoiding memory leaks in this function */ + pfree(optiondatums); + + if (((void *) array) != DatumGetPointer(options)) + pfree(array); +} + /* * Interpret reloptions that are given in text-array format. * @@ -1105,57 +1159,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind, /* Done if no options */ if (PointerIsValid(DatumGetPointer(options))) - { - ArrayType *array = DatumGetArrayTypeP(options); - Datum *optiondatums; - int noptions; + parseRelOptionsInternal(options, validate, reloptions, numoptions); - deconstruct_array(array, TEXTOID, -1, false, 'i', - &optiondatums, NULL, &noptions); + *numrelopts = numoptions; + return reloptions; +} - for (i = 0; i < noptions; i++) - { - char *text_str = VARDATA(optiondatums[i]); - int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ; - int j; +/* Parse local unregistered options. */ +relopt_value * +parseLocalRelOptions(Datum options, bool validate, + relopt_gen *optgen[], int nopts) +{ + relopt_value *values = palloc(sizeof(*values) * nopts); + int i; - /* Search for a match in reloptions */ - for (j = 0; j < numoptions; j++) - { - int kw_len = reloptions[j].gen->namelen; + for (i = 0; i < nopts; i++) + { + values[i].gen = optgen[i]; + values[i].isset = false; + } - if (text_len > kw_len && text_str[kw_len] == '=' && - strncmp(text_str, reloptions[j].gen->name, kw_len) == 0) - { - parse_one_reloption(&reloptions[j], text_str, text_len, - validate); - break; - } - } + if (options != (Datum) 0) + parseRelOptionsInternal(options, validate, values, nopts); - if (j >= numoptions && validate) - { - char *s; - char *p; + return values; +} - s = TextDatumGetCString(optiondatums[i]); - p = strchr(s, '='); - if (p) - *p = '\0'; - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized parameter \"%s\"", s))); - } - } +/* + * Parse local options, allocate a bytea struct that's of the specified + * 'base_size' plus any extra space that's needed for string variables, + * fill its option's fields located at the given offsets and return it. + */ +void * +parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[], + int noptions, size_t base_size, bool validate) +{ + relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions); + relopt_value *vals; + void *opts; + int i; - /* It's worth avoiding memory leaks in this function */ - pfree(optiondatums); - if (((void *) array) != DatumGetPointer(options)) - pfree(array); + for (i = 0; i < noptions; i++) + { + elems[i].optname = optgen[i]->name; + elems[i].opttype = optgen[i]->type; + elems[i].offset = offsets[i]; } - *numrelopts = numoptions; - return reloptions; + vals = parseLocalRelOptions(options, validate, optgen, noptions); + opts = allocateReloptStruct(base_size, vals, noptions); + fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions); + + if (elems) + pfree(elems); + + return opts; } /* diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index aefdd2916d..6cc76914de 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -51,11 +51,14 @@ #include "access/xlog.h" #include "catalog/index.h" #include "catalog/pg_type.h" +#include "commands/defrem.h" #include "pgstat.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/predicate.h" +#include "utils/ruleutils.h" #include "utils/snapmgr.h" +#include "utils/syscache.h" /* ---------------------------------------------------------------- @@ -905,3 +908,81 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, } } } + +/* ---------------- + * index_opclass_options + * + * Parse opclass-specific options for index column. + * ---------------- + */ +bytea * +index_opclass_options(Relation relation, AttrNumber attnum, Datum attoptions, + bool validate) +{ + amopclassoptions_function amopclassoptions = + relation->rd_indam->amopclassoptions; + + if (!amopclassoptions) + { + if (validate && PointerIsValid(DatumGetPointer(attoptions))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("access method \"%s\" does not support opclass options ", + get_am_name(relation->rd_rel->relam)))); + + return NULL; + } + + return amopclassoptions(relation, attnum, attoptions, validate); +} + +/* ---------------- + * index_opclass_options_generic + * + * Parse opclass options for index column using the specified support + * function 'procnum' of column's opclass. + * ---------------- + */ +bytea * +index_opclass_options_generic(Relation indrel, AttrNumber attnum, + uint16 procnum, Datum attoptions, bool validate) +{ + Oid procid = index_getprocid(indrel, attnum, procnum); + FmgrInfo *procinfo; + + if (!OidIsValid(procid)) + { + StringInfoData opclassname; + Oid opclass; + Datum indclassDatum; + oidvector *indclass; + bool isnull; + + if (!DatumGetPointer(attoptions)) + return NULL; /* ok, no options, no procedure */ + + /* + * Report an error if the opclass's options-parsing procedure does not + * exist but the opclass options are specified. + */ + indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple, + Anum_pg_index_indclass, &isnull); + Assert(!isnull); + indclass = (oidvector *) DatumGetPointer(indclassDatum); + opclass = indclass->values[attnum - 1]; + + initStringInfo(&opclassname); + get_opclass_name(opclass, InvalidOid, &opclassname); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("operator class \"%s\" has no options", + opclassname.data))); + } + + procinfo = index_getprocinfo(indrel, attnum, procnum); + + return (bytea *) DatumGetPointer(FunctionCall2(procinfo, + attoptions, + BoolGetDatum(validate))); +} diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 86820eecfc..4a42cb1523 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -694,6 +694,7 @@ CheckAttributeType(const char *attname, void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, + Datum attoptions, CatalogIndexState indstate) { Datum values[Natts_pg_attribute]; @@ -725,10 +726,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation); + values[Anum_pg_attribute_attoptions - 1] = attoptions; /* start out with empty permissions and empty options */ nulls[Anum_pg_attribute_attacl - 1] = true; - nulls[Anum_pg_attribute_attoptions - 1] = true; + nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0; nulls[Anum_pg_attribute_attfdwoptions - 1] = true; nulls[Anum_pg_attribute_attmissingval - 1] = true; @@ -782,7 +784,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* Make sure this is OK, too */ attr->attstattarget = -1; - InsertPgAttributeTuple(rel, attr, indstate); + InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate); /* Add dependency info */ myself.classId = RelationRelationId; @@ -820,7 +822,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* Fill in the correct relation OID in the copied tuple */ attStruct.attrelid = new_rel_oid; - InsertPgAttributeTuple(rel, &attStruct, indstate); + InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate); } } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index d2e4f53a80..125174852b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -26,6 +26,7 @@ #include "access/amapi.h" #include "access/heapam.h" #include "access/multixact.h" +#include "access/reloptions.h" #include "access/relscan.h" #include "access/sysattr.h" #include "access/tableam.h" @@ -106,7 +107,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); -static void AppendAttributeTuples(Relation indexRelation, int numatts); +static void AppendAttributeTuples(Relation indexRelation, int numatts, + Datum *attopts); static void UpdateIndexRelation(Oid indexoid, Oid heapoid, Oid parentIndexId, IndexInfo *indexInfo, @@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation, * ---------------------------------------------------------------- */ static void -AppendAttributeTuples(Relation indexRelation, int numatts) +AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts) { Relation pg_attribute; CatalogIndexState indstate; @@ -508,10 +510,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts) for (i = 0; i < numatts; i++) { Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i); + Datum attoptions = attopts ? attopts[i] : (Datum) 0; Assert(attr->attnum == i + 1); - InsertPgAttributeTuple(pg_attribute, attr, indstate); + InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate); } CatalogCloseIndexes(indstate); @@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid, else predDatum = (Datum) 0; + /* * open the system catalog index relation */ @@ -933,7 +937,8 @@ index_create(Relation heapRelation, /* * append ATTRIBUTE tuples for the index */ - AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); + AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, + indexInfo->ii_OpclassOptions); /* ---------------- * update pg_index @@ -1146,6 +1151,12 @@ index_create(Relation heapRelation, indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs; + /* Validate opclass-specific options */ + if (indexInfo->ii_OpclassOptions) + for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) + (void) index_opclass_options(indexRelation, i + 1, + indexInfo->ii_OpclassOptions[i], true); + /* * If this is bootstrap (initdb) time, then we don't actually fill in the * index yet. We'll be creating more indexes and classes later, so we @@ -2208,6 +2219,10 @@ BuildIndexInfo(Relation index) ii->ii_ExclusionStrats = NULL; } + ii->ii_OpclassOptions = + RelationGetRawOpclassOptions(RelationGetRelid(index), + RelationGetNumberOfAttributes(index)); + /* other info */ ii->ii_Unique = indexStruct->indisunique; ii->ii_ReadyForInserts = indexStruct->indisready; diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index de6282a667..7290731b3d 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; + indexInfo->ii_OpclassOptions = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d05d2fd3d5..23a57be749 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -792,6 +792,7 @@ DefineIndex(Oid relationId, indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; + indexInfo->ii_OpclassOptions = NULL; /* for now */ indexInfo->ii_Unique = stmt->unique; /* In a concurrent build, mark it not-ready-for-inserts */ indexInfo->ii_ReadyForInserts = !stmt->concurrent; @@ -1513,7 +1514,7 @@ CheckPredicate(Expr *predicate) /* * Compute per-index-column information, including indexed column numbers - * or index expressions, opclasses, and indoptions. Note, all output vectors + * or index expressions, opclasses and their options. Note, all output vectors * should be allocated for all columns, including "including" ones. */ static void @@ -1814,6 +1815,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo, accessMethodName))); } + /* Set up the per-column opclass options (attoptions field). */ + if (attribute->opclassopts) + { + Assert(attn < nkeycols); + + if (!indexInfo->ii_OpclassOptions) + indexInfo->ii_OpclassOptions = + palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs); + + indexInfo->ii_OpclassOptions[attn] = + transformRelOptions((Datum) 0, attribute->opclassopts, + NULL, NULL, false, false); + } + attn++; } } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 98519ef836..a7c8fa38a0 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -5688,7 +5688,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); - InsertPgAttributeTuple(attrdesc, &attribute, NULL); + InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL); table_close(attrdesc, RowExclusiveLock); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 78deade89b..f5fad43eba 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2864,6 +2864,7 @@ _copyIndexElem(const IndexElem *from) COPY_STRING_FIELD(indexcolname); COPY_NODE_FIELD(collation); COPY_NODE_FIELD(opclass); + COPY_NODE_FIELD(opclassopts); COPY_SCALAR_FIELD(ordering); COPY_SCALAR_FIELD(nulls_ordering); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4f2ebe5118..6b14095c82 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2546,6 +2546,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b) COMPARE_STRING_FIELD(indexcolname); COMPARE_NODE_FIELD(collation); COMPARE_NODE_FIELD(opclass); + COMPARE_NODE_FIELD(opclassopts); COMPARE_SCALAR_FIELD(ordering); COMPARE_SCALAR_FIELD(nulls_ordering); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 237598e110..35806c0681 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2844,6 +2844,7 @@ _outIndexElem(StringInfo str, const IndexElem *node) WRITE_STRING_FIELD(indexcolname); WRITE_NODE_FIELD(collation); WRITE_NODE_FIELD(opclass); + WRITE_NODE_FIELD(opclassopts); WRITE_ENUM_FIELD(ordering, SortByDir); WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 2405acbf6f..5c6be745c0 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -362,6 +362,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->nulls_first = NULL; } + /* Fetch index opclass options */ + info->opclassoptions = + RelationGetParsedOpclassOptions(indexRelation); + /* * Fetch the index expressions and predicate, if any. We must * modify the copies we obtain from the relcache to have the diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 8311b1dd46..2feade5327 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <alias> alias_clause opt_alias_clause %type <list> func_alias_clause %type <sortby> sortby -%type <ielem> index_elem +%type <ielem> index_elem index_elem_options %type <node> table_ref %type <jexpr> joined_table %type <range> relation_expr @@ -7420,43 +7420,65 @@ index_params: index_elem { $$ = list_make1($1); } | index_params ',' index_elem { $$ = lappend($1, $3); } ; + +index_elem_options: + opt_collate opt_class opt_asc_desc opt_nulls_order + { + $$ = makeNode(IndexElem); + $$->name = NULL; + $$->expr = NULL; + $$->indexcolname = NULL; + $$->collation = $1; + $$->opclass = $2; + $$->opclassopts = NIL; + $$->ordering = $3; + $$->nulls_ordering = $4; + } + | opt_collate any_name reloptions opt_asc_desc opt_nulls_order + { + $$ = makeNode(IndexElem); + $$->name = NULL; + $$->expr = NULL; + $$->indexcolname = NULL; + $$->collation = $1; + $$->opclass = $2; + $$->opclassopts = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; + } + | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order + { + $$ = makeNode(IndexElem); + $$->name = NULL; + $$->expr = NULL; + $$->indexcolname = NULL; + $$->collation = $1; + $$->opclass = NIL; + $$->opclassopts = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; + } + ; + /* * Index attributes can be either simple column references, or arbitrary * expressions in parens. For backwards-compatibility reasons, we allow * an expression that's just a function call to be written without parens. */ -index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order +index_elem: ColId index_elem_options { - $$ = makeNode(IndexElem); + $$ = $2; $$->name = $1; - $$->expr = NULL; - $$->indexcolname = NULL; - $$->collation = $2; - $$->opclass = $3; - $$->ordering = $4; - $$->nulls_ordering = $5; } - | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order + | func_expr_windowless index_elem_options { - $$ = makeNode(IndexElem); - $$->name = NULL; + $$ = $2; $$->expr = $1; - $$->indexcolname = NULL; - $$->collation = $2; - $$->opclass = $3; - $$->ordering = $4; - $$->nulls_ordering = $5; } - | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order + | '(' a_expr ')' index_elem_options { - $$ = makeNode(IndexElem); - $$->name = NULL; + $$ = $4; $$->expr = $2; - $$->indexcolname = NULL; - $$->collation = $4; - $$->opclass = $5; - $$->ordering = $6; - $$->nulls_ordering = $7; } ; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9dda4820af..ea49bef807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -453,8 +453,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc, deparse_context *context); static void get_tablesample_def(TableSampleClause *tablesample, deparse_context *context); -static void get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf); static Node *processIndirection(Node *node, deparse_context *context); static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context); static char *get_relation_name(Oid relid); @@ -469,6 +467,7 @@ static void add_cast_to(StringInfo buf, Oid typid); static char *generate_qualified_type_name(Oid typid); static text *string_to_text(char *str); static char *flatten_reloptions(Oid relid); +static void get_reloptions(StringInfo buf, Datum reloptions); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -1198,6 +1197,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, oidvector *indcollation; oidvector *indclass; int2vector *indoption; + Datum *opcoptions = NULL; StringInfoData buf; char *str; char *sep; @@ -1233,6 +1233,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, Assert(!isnull); indoption = (int2vector *) DatumGetPointer(indoptionDatum); + if (!attrsOnly) + opcoptions = RelationGetRawOpclassOptions(indexrelid, idxrec->indnatts); + /* * Fetch the pg_class tuple of the index relation */ @@ -1369,16 +1372,28 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, if (!attrsOnly && keyno < idxrec->indnkeyatts && (!colno || colno == keyno + 1)) { + bool has_options = false; int16 opt = indoption->values[keyno]; Oid indcoll = indcollation->values[keyno]; + if (opcoptions) + has_options = (opcoptions[keyno] != (Datum) 0); + /* Add collation, if not default for column */ if (OidIsValid(indcoll) && indcoll != keycolcollation) appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcoll))); /* Add the operator class name, if not default */ - get_opclass_name(indclass->values[keyno], keycoltype, &buf); + get_opclass_name(indclass->values[keyno], + has_options ? InvalidOid : keycoltype, &buf); + + if (has_options) + { + appendStringInfoString(&buf, " ("); + get_reloptions(&buf, opcoptions[keyno]); + appendStringInfoChar(&buf, ')'); + } /* Add options if relevant */ if (amroutine->amcanorder) @@ -10459,7 +10474,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context) * actual_datatype. (If you don't want this behavior, just pass * InvalidOid for actual_datatype.) */ -static void +void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf) { @@ -11168,6 +11183,62 @@ string_to_text(char *str) return result; } +/* + * Generate a C string representing a relation options from text[] datum. + */ +static void +get_reloptions(StringInfo buf, Datum reloptions) +{ + Datum *options; + int noptions; + int i; + + deconstruct_array(DatumGetArrayTypeP(reloptions), + TEXTOID, -1, false, 'i', + &options, NULL, &noptions); + + for (i = 0; i < noptions; i++) + { + char *option = TextDatumGetCString(options[i]); + char *name; + char *separator; + char *value; + + /* + * Each array element should have the form name=value. If the "=" + * is missing for some reason, treat it like an empty value. + */ + name = option; + separator = strchr(option, '='); + if (separator) + { + *separator = '\0'; + value = separator + 1; + } + else + value = ""; + + if (i > 0) + appendStringInfoString(buf, ", "); + appendStringInfo(buf, "%s=", quote_identifier(name)); + + /* + * In general we need to quote the value; but to avoid unnecessary + * clutter, do not quote if it is an identifier that would not + * need quoting. (We could also allow numbers, but that is a bit + * trickier than it looks --- for example, are leading zeroes + * significant? We don't want to assume very much here about what + * custom reloptions might mean.) + */ + if (quote_identifier(value) == value) + appendStringInfoString(buf, value); + else + simple_quote_literal(buf, value); + + pfree(option); + } +} + /* * Generate a C string representing a relation's reloptions, or NULL if none. */ @@ -11188,56 +11259,9 @@ flatten_reloptions(Oid relid) if (!isnull) { StringInfoData buf; - Datum *options; - int noptions; - int i; initStringInfo(&buf); - - deconstruct_array(DatumGetArrayTypeP(reloptions), - TEXTOID, -1, false, 'i', - &options, NULL, &noptions); - - for (i = 0; i < noptions; i++) - { - char *option = TextDatumGetCString(options[i]); - char *name; - char *separator; - char *value; - - /* - * Each array element should have the form name=value. If the "=" - * is missing for some reason, treat it like an empty value. - */ - name = option; - separator = strchr(option, '='); - if (separator) - { - *separator = '\0'; - value = separator + 1; - } - else - value = ""; - - if (i > 0) - appendStringInfoString(&buf, ", "); - appendStringInfo(&buf, "%s=", quote_identifier(name)); - - /* - * In general we need to quote the value; but to avoid unnecessary - * clutter, do not quote if it is an identifier that would not - * need quoting. (We could also allow numbers, but that is a bit - * trickier than it looks --- for example, are leading zeroes - * significant? We don't want to assume very much here about what - * custom reloptions might mean.) - */ - if (quote_identifier(value) == value) - appendStringInfoString(&buf, value); - else - simple_quote_literal(&buf, value); - - pfree(option); - } + get_reloptions(&buf, reloptions); result = buf.data; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 2b992d7832..62caa4cfaf 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -5173,6 +5173,105 @@ GetRelationPublicationActions(Relation relation) return pubactions; } +/* + * RelationGetIndexOpclassOptions -- get opclass-specific options for the index + */ +Datum * +RelationGetRawOpclassOptions(Oid indexrelid, int16 natts) +{ + Datum *options = NULL; + int16 attnum; + + for (attnum = 1; attnum <= natts; attnum++) + { + HeapTuple tuple; + Datum attopts; + bool isnull; + + tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(indexrelid), + Int16GetDatum(attnum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, indexrelid); + + attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions, + &isnull); + + if (!isnull) + { + if (!options) + options = palloc0(sizeof(Datum) * natts); + + options[attnum - 1] = datumCopy(attopts, false, -1); /* text */ + } + + ReleaseSysCache(tuple); + } + + return options; +} + +/* + * RelationGetOpclassOptions -- get parsed opclass-specific options for an index + */ +bytea ** +RelationGetParsedOpclassOptions(Relation relation) +{ + MemoryContext oldcxt; + bytea **opts; + Datum *rawopts; + int natts = RelationGetNumberOfAttributes(relation); + int i; + + /* Try to copy cached options. */ + if (relation->rd_opcoptions) + { + opts = palloc(sizeof(*opts) * natts); + + for (i = 0; i < natts; i++) + { + bytea *opt = relation->rd_opcoptions[i]; + + opts[i] = !opt ? NULL : (bytea *) + DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1)); + } + + return opts; + } + + /* Get and parse opclass options. */ + opts = palloc0(sizeof(*opts) * natts); + + rawopts = RelationGetRawOpclassOptions(RelationGetRelid(relation), natts); + + for (i = 0; i < natts; i++) + { + Datum options = rawopts ? rawopts[i] : (Datum) 0; + + opts[i] = index_opclass_options(relation, i + 1, options, false); + + if (options != (Datum) 0) + pfree(DatumGetPointer(options)); + } + + if (rawopts) + pfree(rawopts); + + /* Copy parsed options to the cache. */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + + relation->rd_opcoptions = palloc(sizeof(*opts) * natts); + + for (i = 0; i < natts; i++) + relation->rd_opcoptions[i] = !opts[i] ? NULL : (bytea *) + DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1)); + + MemoryContextSwitchTo(oldcxt); + + return opts; +} + /* * Routines to support ereport() reports of relation-related errors * diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 6e3db06eed..9c06d1a094 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root, typedef bytea *(*amoptions_function) (Datum reloptions, bool validate); +/* parse column opclass-specific options */ +typedef bytea *(*amopclassoptions_function) (Relation index, + AttrNumber colno, + Datum attoptions, + bool validate); + /* report AM, index, or index column property */ typedef bool (*amproperty_function) (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, @@ -215,6 +221,7 @@ typedef struct IndexAmRoutine amcanreturn_function amcanreturn; /* can be NULL */ amcostestimate_function amcostestimate; amoptions_function amoptions; + amopclassoptions_function amopclassoptions; amproperty_function amproperty; /* can be NULL */ ambuildphasename_function ambuildphasename; /* can be NULL */ amvalidate_function amvalidate; diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 8c053be2ca..aa170f6de6 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -180,6 +180,11 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, extern void index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, double *distances, bool recheckOrderBy); +extern bytea *index_opclass_options(Relation relation, AttrNumber attnum, + Datum attoptions, bool validate); +extern bytea *index_opclass_options_generic(Relation relation, + AttrNumber attnum, uint16 procnum, + Datum attoptions, bool validate); /* * index access method support routines (in genam.c) diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index a1912f41e6..8dea2d8e69 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -263,12 +263,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, amoptions_function amoptions); extern relopt_value *parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts); +extern relopt_value *parseLocalRelOptions(Datum options, bool validate, + relopt_gen **gen, int nelems); extern void *allocateReloptStruct(Size base, relopt_value *options, int numoptions); extern void fillRelOptions(void *rdopts, Size basesize, relopt_value *options, int numoptions, bool validate, const relopt_parse_elt *elems, int nelems); +extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], + int offsets[], int noptions, size_t base_size, + bool validate); extern bytea *default_reloptions(Datum reloptions, bool validate, relopt_kind kind); diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index eec71c29d5..866e94d04f 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -94,6 +94,7 @@ extern List *heap_truncate_find_FKs(List *relationIds); extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, + Datum attoptions, CatalogIndexState indstate); extern void InsertPgClassTuple(Relation pg_class_desc, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 99b9fa414f..e0d6c6c375 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -138,6 +138,7 @@ typedef struct ExprState * UniqueProcs * UniqueStrats * Unique is it a unique index? + * OpclassOptions opclass-specific options, or NULL if none * ReadyForInserts is it valid for inserts? * Concurrent are we doing a concurrent index build? * BrokenHotChain did we detect any broken HOT chains? @@ -166,6 +167,7 @@ typedef struct IndexInfo Oid *ii_UniqueOps; /* array with one entry per column */ Oid *ii_UniqueProcs; /* array with one entry per column */ uint16 *ii_UniqueStrats; /* array with one entry per column */ + Datum *ii_OpclassOptions; /* array with one entry per column */ bool ii_Unique; bool ii_ReadyForInserts; bool ii_Concurrent; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2a8edf934f..15699971a7 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -701,6 +701,7 @@ typedef struct IndexElem char *indexcolname; /* name for index column; NULL = default */ List *collation; /* name of collation; NIL = default */ List *opclass; /* name of desired opclass; NIL = default */ + List *opclassopts; /* opclass-specific options, or NIL */ SortByDir ordering; /* ASC/DESC/default */ SortByNulls nulls_ordering; /* FIRST/LAST/default */ } IndexElem; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 4b7703d478..f8a935f501 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -802,6 +802,7 @@ struct IndexOptInfo Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */ bool *reverse_sort; /* is sort order descending? */ bool *nulls_first; /* do NULLs come first in the sort order? */ + bytea **opclassoptions; /* opclass-specific options for columns */ bool *canreturn; /* which index cols can be returned in an * index-only scan? */ Oid relam; /* OID of the access method (in pg_am) */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index d7f33abce3..8440e225ee 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -175,6 +175,7 @@ typedef struct RelationData uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */ void *rd_amcache; /* available for use by index AM */ Oid *rd_indcollation; /* OIDs of index collations */ + bytea **rd_opcoptions; /* parsed opclass-specific options */ /* * foreign-table support diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index d9c10ffcba..562929c058 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -14,6 +14,7 @@ #ifndef RELCACHE_H #define RELCACHE_H +#include "postgres.h" #include "access/tupdesc.h" #include "nodes/bitmapset.h" @@ -49,6 +50,8 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation); extern Oid RelationGetReplicaIndex(Relation relation); extern List *RelationGetIndexExpressions(Relation relation); extern List *RelationGetIndexPredicate(Relation relation); +extern bytea **RelationGetParsedOpclassOptions(Relation relation); +extern Datum *RelationGetRawOpclassOptions(Oid indexrelid, int16 natts); typedef enum IndexAttrBitmapKind { diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h index d34cad2f4b..68d7ca892e 100644 --- a/src/include/utils/ruleutils.h +++ b/src/include/utils/ruleutils.h @@ -35,5 +35,7 @@ extern List *select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used); extern char *generate_collation_name(Oid collid); extern char *get_range_partbound_string(List *bound_datums); +extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); + #endif /* RULEUTILS_H */ diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out index acab8e0b11..33f2bf0e3f 100644 --- a/src/test/regress/expected/btree_index.out +++ b/src/test/regress/expected/btree_index.out @@ -244,6 +244,11 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; {vacuum_cleanup_index_scale_factor=70.0} (1 row) +-- Test unsupported btree opclass parameters +create index on btree_tall_tbl (id int4_ops(foo=1)); +ERROR: access method "btree" does not support opclass options +create index on btree_tall_tbl (id default(foo=1)); +ERROR: access method "btree" does not support opclass options -- -- Test for multilevel page deletion -- diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql index 48eaf4fe42..aecd690a01 100644 --- a/src/test/regress/sql/btree_index.sql +++ b/src/test/regress/sql/btree_index.sql @@ -121,6 +121,10 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0); select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; +-- Test unsupported btree opclass parameters +create index on btree_tall_tbl (id int4_ops(foo=1)); +create index on btree_tall_tbl (id default(foo=1)); + -- -- Test for multilevel page deletion -- -- 2.20.1
0002-Add-opclass-parameters-to-GiST-20190611.patch (text/plain, 13.7K) download From ddd99a8fb48774cfaf9ca151ebb8a5238bc1d2b3 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 21:17:42 +0200 Subject: [PATCH 02/10] Add opclass parameters to GiST --- doc/src/sgml/xindex.sgml | 5 +++ src/backend/access/gist/gist.c | 2 ++ src/backend/access/gist/gistget.c | 12 ++++--- src/backend/access/gist/gistsplit.c | 15 +++++---- src/backend/access/gist/gistutil.c | 44 ++++++++++++++++++-------- src/backend/access/gist/gistvalidate.c | 34 +++++++++++++------- src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 4 +++ 8 files changed, 82 insertions(+), 37 deletions(-) diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml index 9446f8b836..8c5b5289d7 100644 --- a/doc/src/sgml/xindex.sgml +++ b/doc/src/sgml/xindex.sgml @@ -546,6 +546,11 @@ index-only scans (optional)</entry> <entry>9</entry> </row> + <row> + <entry><function>options</function></entry> + <entry>parse opclass-specific options (optional)</entry> + <entry>10</entry> + </row> </tbody> </tgroup> </table> diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 470b121e7d..8e3460f456 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS) amroutine->amestimateparallelscan = NULL; amroutine->aminitparallelscan = NULL; amroutine->amparallelrescan = NULL; + amroutine->amopclassoptions = gistopclassoptions; PG_RETURN_POINTER(amroutine); } @@ -1528,6 +1529,7 @@ initGISTstate(Relation index) giststate->scanCxt = scanCxt; giststate->tempCxt = scanCxt; /* caller must change this if needed */ giststate->leafTupdesc = index->rd_att; + giststate->opclassoptions = RelationGetParsedOpclassOptions(index); /* * The truncated tupdesc for non-leaf index tuples, which doesn't contain diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 8108fbb7d8..abfe659ca5 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan, Datum test; bool recheck; GISTENTRY de; + bytea *options = giststate->opclassoptions[key->sk_attno - 1]; gistdentryinit(giststate, key->sk_attno - 1, &de, datum, r, page, offset, @@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan, */ recheck = true; - test = FunctionCall5Coll(&key->sk_func, + test = FunctionCall6Coll(&key->sk_func, key->sk_collation, PointerGetDatum(&de), key->sk_argument, Int16GetDatum(key->sk_strategy), ObjectIdGetDatum(key->sk_subtype), - PointerGetDatum(&recheck)); + PointerGetDatum(&recheck), + PointerGetDatum(options)); if (!DatumGetBool(test)) return false; @@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan, Datum dist; bool recheck; GISTENTRY de; + bytea *options = giststate->opclassoptions[key->sk_attno - 1]; gistdentryinit(giststate, key->sk_attno - 1, &de, datum, r, page, offset, @@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan, * about the flag, but are expected to never be lossy. */ recheck = false; - dist = FunctionCall5Coll(&key->sk_func, + dist = FunctionCall6Coll(&key->sk_func, key->sk_collation, PointerGetDatum(&de), key->sk_argument, Int16GetDatum(key->sk_strategy), ObjectIdGetDatum(key->sk_subtype), - PointerGetDatum(&recheck)); + PointerGetDatum(&recheck), + PointerGetDatum(options)); *recheck_distances_p |= recheck; *distance_p = DatumGetFloat8(dist); } diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c index 6a9c54d86c..2b3cb967e1 100644 --- a/src/backend/access/gist/gistsplit.c +++ b/src/backend/access/gist/gistsplit.c @@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC evec->n = v->spl_nleft; memcpy(evec->vector, entryvec->vector + FirstOffsetNumber, sizeof(GISTENTRY) * evec->n); - v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno], + v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno], giststate->supportCollation[attno], PointerGetDatum(evec), - PointerGetDatum(&nbytes)); + PointerGetDatum(&nbytes), + PointerGetDatum(giststate->opclassoptions[attno])); evec->n = v->spl_nright; memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft, sizeof(GISTENTRY) * evec->n); - v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno], + v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno], giststate->supportCollation[attno], PointerGetDatum(evec), - PointerGetDatum(&nbytes)); + PointerGetDatum(&nbytes), + PointerGetDatum(giststate->opclassoptions[attno])); } /* @@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec * Let the opclass-specific PickSplit method do its thing. Note that at * this point we know there are no null keys in the entryvec. */ - FunctionCall2Coll(&giststate->picksplitFn[attno], + FunctionCall3Coll(&giststate->picksplitFn[attno], giststate->supportCollation[attno], PointerGetDatum(entryvec), - PointerGetDatum(sv)); + PointerGetDatum(sv), + PointerGetDatum(giststate->opclassoptions[attno])); if (sv->spl_nleft == 0 || sv->spl_nright == 0) { diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 49df05653b..000f10103c 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -201,10 +201,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, } /* Make union and store in attr array */ - attr[i] = FunctionCall2Coll(&giststate->unionFn[i], + attr[i] = FunctionCall3Coll(&giststate->unionFn[i], giststate->supportCollation[i], PointerGetDatum(evec), - PointerGetDatum(&attrsize)); + PointerGetDatum(&attrsize), + PointerGetDatum(giststate->opclassoptions[i])); isnull[i] = false; } @@ -270,10 +271,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno, } *dstisnull = false; - *dst = FunctionCall2Coll(&giststate->unionFn[attno], + *dst = FunctionCall3Coll(&giststate->unionFn[attno], giststate->supportCollation[attno], PointerGetDatum(evec), - PointerGetDatum(&dstsize)); + PointerGetDatum(&dstsize), + PointerGetDatum(giststate->opclassoptions[attno])); } } @@ -282,10 +284,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b) { bool result; - FunctionCall3Coll(&giststate->equalFn[attno], + FunctionCall4Coll(&giststate->equalFn[attno], giststate->supportCollation[attno], a, b, - PointerGetDatum(&result)); + PointerGetDatum(&result), + PointerGetDatum(giststate->opclassoptions[attno])); return result; } @@ -559,9 +562,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, return; dep = (GISTENTRY *) - DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey], + DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey], giststate->supportCollation[nkey], - PointerGetDatum(e))); + PointerGetDatum(e), + PointerGetDatum(giststate->opclassoptions[nkey]))); /* decompressFn may just return the given pointer */ if (dep != e) gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset, @@ -596,9 +600,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r, /* there may not be a compress function in opclass */ if (OidIsValid(giststate->compressFn[i].fn_oid)) cep = (GISTENTRY *) - DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i], + DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i], giststate->supportCollation[i], - PointerGetDatum(&centry))); + PointerGetDatum(&centry), + PointerGetDatum(giststate->opclassoptions[i]))); else cep = &centry; compatt[i] = cep->key; @@ -643,9 +648,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r) gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false); fep = (GISTENTRY *) - DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey], + DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey], giststate->supportCollation[nkey], - PointerGetDatum(&fentry))); + PointerGetDatum(&fentry), + PointerGetDatum(giststate->opclassoptions[nkey]))); /* fetchFn set 'key', return it to the caller */ return fep->key; @@ -722,11 +728,12 @@ gistpenalty(GISTSTATE *giststate, int attno, if (giststate->penaltyFn[attno].fn_strict == false || (isNullOrig == false && isNullAdd == false)) { - FunctionCall3Coll(&giststate->penaltyFn[attno], + FunctionCall4Coll(&giststate->penaltyFn[attno], giststate->supportCollation[attno], PointerGetDatum(orig), PointerGetDatum(add), - PointerGetDatum(&penalty)); + PointerGetDatum(&penalty), + PointerGetDatum(giststate->opclassoptions[attno])); /* disallow negative or NaN penalty */ if (isnan(penalty) || penalty < 0.0) penalty = 0.0; @@ -915,6 +922,15 @@ gistoptions(Datum reloptions, bool validate) return (bytea *) rdopts; } +bytea * +gistopclassoptions(Relation index, AttrNumber attnum, Datum attoptions, + bool validate) +{ + return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC, + attoptions, validate); +} + + /* * gistproperty() -- Check boolean properties of indexes. * diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index dfc1a87a75..7ae820070b 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid) { case GIST_CONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, BOOLOID, false, - 5, 5, INTERNALOID, opcintype, - INT2OID, OIDOID, INTERNALOID); + 5, 6, INTERNALOID, opcintype, + INT2OID, OIDOID, INTERNALOID, + INTERNALOID); break; case GIST_UNION_PROC: ok = check_amproc_signature(procform->amproc, opckeytype, false, - 2, 2, INTERNALOID, INTERNALOID); + 2, 3, INTERNALOID, INTERNALOID, + INTERNALOID); break; case GIST_COMPRESS_PROC: case GIST_DECOMPRESS_PROC: case GIST_FETCH_PROC: ok = check_amproc_signature(procform->amproc, INTERNALOID, true, - 1, 1, INTERNALOID); + 1, 2, INTERNALOID, INTERNALOID); break; case GIST_PENALTY_PROC: ok = check_amproc_signature(procform->amproc, INTERNALOID, true, - 3, 3, INTERNALOID, - INTERNALOID, INTERNALOID); + 3, 4, INTERNALOID, + INTERNALOID, INTERNALOID, + INTERNALOID); break; case GIST_PICKSPLIT_PROC: ok = check_amproc_signature(procform->amproc, INTERNALOID, true, - 2, 2, INTERNALOID, INTERNALOID); + 2, 3, INTERNALOID, INTERNALOID, + INTERNALOID); break; case GIST_EQUAL_PROC: ok = check_amproc_signature(procform->amproc, INTERNALOID, false, - 3, 3, opckeytype, opckeytype, - INTERNALOID); + 3, 4, opckeytype, opckeytype, + INTERNALOID, INTERNALOID); break; case GIST_DISTANCE_PROC: ok = check_amproc_signature(procform->amproc, FLOAT8OID, false, - 5, 5, INTERNALOID, opcintype, - INT2OID, OIDOID, INTERNALOID); + 5, 6, INTERNALOID, opcintype, + INT2OID, OIDOID, INTERNALOID, + INTERNALOID); + break; + case GIST_OPCLASSOPT_PROC: + ok = check_amproc_signature(procform->amproc, INTERNALOID, false, + 2, 2, INTERNALOID, BOOLOID); break; default: ereport(INFO, @@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid) (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || - i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC) + i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || + i == GIST_OPCLASSOPT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 6902f4115b..0c57a64369 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -34,7 +34,8 @@ #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 -#define GISTNProcs 9 +#define GIST_OPCLASSOPT_PROC 10 +#define GISTNProcs 10 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f80694bf9a..d46b6b89f9 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -96,6 +96,8 @@ typedef struct GISTSTATE /* Collations to pass to the support functions */ Oid supportCollation[INDEX_MAX_KEYS]; + + bytea **opclassoptions; /* parsed opclass-specific options */ } GISTSTATE; @@ -462,6 +464,8 @@ extern bool gistvalidate(Oid opclassoid); #define GIST_DEFAULT_FILLFACTOR 90 extern bytea *gistoptions(Datum reloptions, bool validate); +extern bytea *gistopclassoptions(Relation index, AttrNumber colno, + Datum options, bool validate); extern bool gistproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull); -- 2.20.1
0003-Add-opclass-parameters-to-GIN-20190611.patch (text/plain, 15.2K) download From f056cb2deddde2ff0cbef894be2699710720507b Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 21:18:18 +0200 Subject: [PATCH 03/10] Add opclass parameters to GIN --- doc/src/sgml/xindex.sgml | 7 ++++++ src/backend/access/gin/ginget.c | 11 +++++----- src/backend/access/gin/ginlogic.c | 15 +++++++------ src/backend/access/gin/ginscan.c | 6 ++++-- src/backend/access/gin/ginutil.c | 27 +++++++++++++++++------ src/backend/access/gin/ginvalidate.c | 32 +++++++++++++++++----------- src/backend/utils/adt/selfuncs.c | 6 ++++-- src/include/access/gin.h | 3 ++- src/include/access/gin_private.h | 5 +++++ 9 files changed, 78 insertions(+), 34 deletions(-) diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml index 8c5b5289d7..658ec9bc5a 100644 --- a/doc/src/sgml/xindex.sgml +++ b/doc/src/sgml/xindex.sgml @@ -665,6 +665,13 @@ </entry> <entry>6</entry> </row> + <row> + <entry><function>options</function></entry> + <entry> + parse opclass-specific options (optional) + </entry> + <entry>7</entry> + </row> </tbody> </tgroup> </table> diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index b18ae2b3ed..547b7b4762 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, * case cmp < 0 => not match and continue scan *---------- */ - cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1], + cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1], btree->ginstate->supportCollation[attnum - 1], scanEntry->queryKey, idatum, UInt16GetDatum(scanEntry->strategy), - PointerGetDatum(scanEntry->extra_data))); - + PointerGetDatum(scanEntry->extra_data), + PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1]))); if (cmp > 0) return true; else if (cmp < 0) @@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page, * case cmp < 0 => not match and continue scan *---------- */ - cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1], + cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1], ginstate->supportCollation[entry->attnum - 1], entry->queryKey, datum[off - 1], UInt16GetDatum(entry->strategy), - PointerGetDatum(entry->extra_data))); + PointerGetDatum(entry->extra_data), + PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1]))); if (cmp == 0) return true; else if (cmp > 0) diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c index 8f85978972..028b29a8c0 100644 --- a/src/backend/access/gin/ginlogic.c +++ b/src/backend/access/gin/ginlogic.c @@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key) */ key->recheckCurItem = true; - return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo, + return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo, key->collation, PointerGetDatum(key->entryRes), UInt16GetDatum(key->strategy), @@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key) PointerGetDatum(key->extra_data), PointerGetDatum(&key->recheckCurItem), PointerGetDatum(key->queryValues), - PointerGetDatum(key->queryCategories))); + PointerGetDatum(key->queryCategories), + PointerGetDatum(key->opclassOptions))); } /* @@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key) static GinTernaryValue directTriConsistentFn(GinScanKey key) { - return DatumGetGinTernaryValue(FunctionCall7Coll( + return DatumGetGinTernaryValue(FunctionCall8Coll( key->triConsistentFmgrInfo, key->collation, PointerGetDatum(key->entryRes), @@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key) UInt32GetDatum(key->nuserentries), PointerGetDatum(key->extra_data), PointerGetDatum(key->queryValues), - PointerGetDatum(key->queryCategories))); + PointerGetDatum(key->queryCategories), + PointerGetDatum(key->opclassOptions))); } /* @@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key) { GinTernaryValue result; - result = DatumGetGinTernaryValue(FunctionCall7Coll( + result = DatumGetGinTernaryValue(FunctionCall8Coll( key->triConsistentFmgrInfo, key->collation, PointerGetDatum(key->entryRes), @@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key) UInt32GetDatum(key->nuserentries), PointerGetDatum(key->extra_data), PointerGetDatum(key->queryValues), - PointerGetDatum(key->queryCategories))); + PointerGetDatum(key->queryCategories), + PointerGetDatum(key->opclassOptions))); if (result == GIN_MAYBE) { key->recheckCurItem = true; diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c index 74d9821ac1..d1384a33d4 100644 --- a/src/backend/access/gin/ginscan.c +++ b/src/backend/access/gin/ginscan.c @@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, key->strategy = strategy; key->searchMode = searchMode; key->attnum = attnum; + key->opclassOptions = ginstate->opclassOptions[attnum - 1]; ItemPointerSetMin(&key->curItem); key->curItemMatches = false; @@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan) /* OK to call the extractQueryFn */ queryValues = (Datum *) - DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1], + DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1], so->ginstate.supportCollation[skey->sk_attno - 1], skey->sk_argument, PointerGetDatum(&nQueryValues), @@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan) PointerGetDatum(&partial_matches), PointerGetDatum(&extra_data), PointerGetDatum(&nullFlags), - PointerGetDatum(&searchMode))); + PointerGetDatum(&searchMode), + PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1]))); /* * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index cf9699ad18..03874909b0 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->amcanreturn = NULL; amroutine->amcostestimate = gincostestimate; amroutine->amoptions = ginoptions; + amroutine->amopclassoptions = ginopclassoptions; amroutine->amproperty = NULL; amroutine->ambuildphasename = NULL; amroutine->amvalidate = ginvalidate; @@ -96,6 +97,7 @@ initGinState(GinState *state, Relation index) state->index = index; state->oneCol = (origTupdesc->natts == 1) ? true : false; state->origTupdesc = origTupdesc; + state->opclassOptions = RelationGetParsedOpclassOptions(index); for (i = 0; i < origTupdesc->natts; i++) { @@ -399,9 +401,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum, return 0; /* both not null, so safe to call the compareFn */ - return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1], + return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1], ginstate->supportCollation[attnum - 1], - a, b)); + a, b, + PointerGetDatum(ginstate->opclassOptions[attnum - 1]))); } /* @@ -437,6 +440,7 @@ typedef struct { FmgrInfo *cmpDatumFunc; Oid collation; + Datum options; bool haveDups; } cmpEntriesArg; @@ -458,9 +462,10 @@ cmpEntries(const void *a, const void *b, void *arg) else if (bb->isnull) res = -1; /* not-NULL "<" NULL */ else - res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc, + res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc, data->collation, - aa->datum, bb->datum)); + aa->datum, bb->datum, + data->options)); /* * Detect if we have any duplicates. If there are equal keys, qsort must @@ -506,11 +511,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum, /* OK, call the opclass's extractValueFn */ nullFlags = NULL; /* in case extractValue doesn't set it */ entries = (Datum *) - DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1], + DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1], ginstate->supportCollation[attnum - 1], value, PointerGetDatum(nentries), - PointerGetDatum(&nullFlags))); + PointerGetDatum(&nullFlags), + PointerGetDatum(ginstate->opclassOptions[attnum - 1]))); /* * Generate a placeholder if the item contained no keys. @@ -553,6 +559,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum, arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1]; arg.collation = ginstate->supportCollation[attnum - 1]; + arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]); arg.haveDups = false; qsort_arg(keydata, *nentries, sizeof(keyEntryData), cmpEntries, (void *) &arg); @@ -628,6 +635,14 @@ ginoptions(Datum reloptions, bool validate) return (bytea *) rdopts; } +bytea * +ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions, + bool validate) +{ + return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC, + attoptions, validate); +} + /* * Fetch index's statistical data into *stats * diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c index 63bd7f2adc..a000052f09 100644 --- a/src/backend/access/gin/ginvalidate.c +++ b/src/backend/access/gin/ginvalidate.c @@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid) { case GIN_COMPARE_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, - 2, 2, opckeytype, opckeytype); + 2, 3, opckeytype, opckeytype, + INTERNALOID); break; case GIN_EXTRACTVALUE_PROC: /* Some opclasses omit nullFlags */ ok = check_amproc_signature(procform->amproc, INTERNALOID, false, - 2, 3, opcintype, INTERNALOID, - INTERNALOID); + 2, 4, opcintype, INTERNALOID, + INTERNALOID, INTERNALOID); break; case GIN_EXTRACTQUERY_PROC: /* Some opclasses omit nullFlags and searchMode */ ok = check_amproc_signature(procform->amproc, INTERNALOID, false, - 5, 7, opcintype, INTERNALOID, + 5, 8, opcintype, INTERNALOID, INT2OID, INTERNALOID, INTERNALOID, - INTERNALOID, INTERNALOID); + INTERNALOID, INTERNALOID, + INTERNALOID); break; case GIN_CONSISTENT_PROC: /* Some opclasses omit queryKeys and nullFlags */ ok = check_amproc_signature(procform->amproc, BOOLOID, false, - 6, 8, INTERNALOID, INT2OID, + 6, 9, INTERNALOID, INT2OID, opcintype, INT4OID, INTERNALOID, INTERNALOID, - INTERNALOID, INTERNALOID); + INTERNALOID, INTERNALOID, + INTERNALOID); break; case GIN_COMPARE_PARTIAL_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, - 4, 4, opckeytype, opckeytype, - INT2OID, INTERNALOID); + 4, 5, opckeytype, opckeytype, + INT2OID, INTERNALOID, INTERNALOID); break; case GIN_TRICONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, CHAROID, false, - 7, 7, INTERNALOID, INT2OID, + 7, 8, INTERNALOID, INT2OID, opcintype, INT4OID, INTERNALOID, INTERNALOID, - INTERNALOID); + INTERNALOID, INTERNALOID); + break; + case GIN_OPCLASSOPTIONS_PROC: + ok = check_amproc_signature(procform->amproc, INTERNALOID, + false, 2, 2, INTERNALOID, BOOLOID); break; default: ereport(INFO, @@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid) if (opclassgroup && (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ - if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC) + if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC || + i == GIN_OPCLASSOPTIONS_PROC) continue; /* optional method */ if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC) continue; /* don't need both, see check below loop */ diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d7e3f09f1a..d491bdd7ae 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -135,6 +135,7 @@ #include "utils/lsyscache.h" #include "utils/pg_locale.h" #include "utils/rel.h" +#include "utils/relcache.h" #include "utils/selfuncs.h" #include "utils/snapmgr.h" #include "utils/spccache.h" @@ -6242,7 +6243,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol, else collation = DEFAULT_COLLATION_OID; - OidFunctionCall7Coll(extractProcOid, + OidFunctionCall8Coll(extractProcOid, collation, query, PointerGetDatum(&nentries), @@ -6250,7 +6251,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol, PointerGetDatum(&partial_matches), PointerGetDatum(&extra_data), PointerGetDatum(&nullFlags), - PointerGetDatum(&searchMode)); + PointerGetDatum(&searchMode), + PointerGetDatum(index->opclassoptions[indexcol])); if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT) { diff --git a/src/include/access/gin.h b/src/include/access/gin.h index a8eef5a379..aabfbcd5ce 100644 --- a/src/include/access/gin.h +++ b/src/include/access/gin.h @@ -25,7 +25,8 @@ #define GIN_CONSISTENT_PROC 4 #define GIN_COMPARE_PARTIAL_PROC 5 #define GIN_TRICONSISTENT_PROC 6 -#define GINNProcs 6 +#define GIN_OPCLASSOPTIONS_PROC 7 +#define GINNProcs 7 /* * searchMode settings for extractQueryFn. diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index afb3e15721..0f626a7dbb 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -67,6 +67,8 @@ typedef struct GinState TupleDesc origTupdesc; TupleDesc tupdesc[INDEX_MAX_KEYS]; + bytea **opclassOptions; /* per-index-column opclass options */ + /* * Per-index-column opclass support functions */ @@ -85,6 +87,8 @@ typedef struct GinState /* ginutil.c */ extern bytea *ginoptions(Datum reloptions, bool validate); +extern bytea *ginopclassoptions(Relation index, AttrNumber colno, + Datum attoptions, bool validate); extern void initGinState(GinState *state, Relation index); extern Buffer GinNewBuffer(Relation index); extern void GinInitBuffer(Buffer b, uint32 f); @@ -297,6 +301,7 @@ typedef struct GinScanKeyData StrategyNumber strategy; int32 searchMode; OffsetNumber attnum; + bytea *opclassOptions; /* * Match status data. curItem is the TID most recently tested (could be a -- 2.20.1
0004-Add-opclass-parameters-to-GiST-tsvector_ops-20190611.patch (text/plain, 31.7K) download From ae97174555cbaf059d2df45df3c80d7e67007fdd Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 21:38:42 +0200 Subject: [PATCH 04/10] Add opclass parameters to GiST tsvector_ops --- doc/src/sgml/textsearch.sgml | 9 +- src/backend/utils/adt/tsgistidx.c | 269 ++++++++++++++------------ src/include/catalog/pg_amproc.dat | 5 +- src/include/catalog/pg_proc.dat | 19 +- src/test/regress/expected/tsearch.out | 176 +++++++++++++++++ src/test/regress/sql/tsearch.sql | 45 +++++ 6 files changed, 392 insertions(+), 131 deletions(-) diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml index 40888a4d20..54b796ecf1 100644 --- a/doc/src/sgml/textsearch.sgml +++ b/doc/src/sgml/textsearch.sgml @@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars'); <tertiary>text search</tertiary> </indexterm> - <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal> + <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal> </term> <listitem> @@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars'); Creates a GiST (Generalized Search Tree)-based index. The <replaceable>column</replaceable> can be of <type>tsvector</type> or <type>tsquery</type> type. + Optional integer parameter <literal>siglen</literal> determines + signature length in bytes (see below for details). </para> </listitem> </varlistentry> @@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars'); to check the actual table row to eliminate such false matches. (<productname>PostgreSQL</productname> does this automatically when needed.) GiST indexes are lossy because each document is represented in the - index by a fixed-length signature. The signature is generated by hashing + index by a fixed-length signature. Signature length in bytes is determined + by the value of the optional integer parameter <literal>siglen</literal>. + Default signature length (when <literal>siglen</literal> is not specied) is + 124 bytes, maximal length is 484 bytes. The signature is generated by hashing each word into a single bit in an n-bit string, with all these bits OR-ed together to produce an n-bit document signature. When two words hash to the same bit position there will be a false match. If all words in diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c index 4f256260fd..91661cf8b8 100644 --- a/src/backend/utils/adt/tsgistidx.c +++ b/src/backend/utils/adt/tsgistidx.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "access/gist.h" +#include "access/reloptions.h" #include "access/tuptoaster.h" #include "port/pg_bitutils.h" #include "tsearch/ts_utils.h" @@ -22,17 +23,22 @@ #include "utils/pg_crc.h" -#define SIGLENINT 31 /* >121 => key will toast, so it will not work - * !!! */ +#define SIGLEN_DEFAULT (31 * 4) +#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */ -#define SIGLEN ( sizeof(int32) * SIGLENINT ) -#define SIGLENBIT (SIGLEN * BITS_PER_BYTE) +#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE) + +/* tsvector_ops opclass options */ +typedef struct GistTsVectorOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int siglen; /* signature length */ +} GistTsVectorOptions; -typedef char BITVEC[SIGLEN]; typedef char *BITVECP; -#define LOOPBYTE \ - for(i=0;i<SIGLEN;i++) +#define LOOPBYTE(siglen) \ + for (i = 0; i < siglen; i++) #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) ) #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 ) @@ -40,8 +46,8 @@ typedef char *BITVECP; #define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) ) #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 ) -#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT) -#define HASH(sign, val) SETBIT((sign), HASHVAL(val)) +#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen)) +#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen)) #define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key)) @@ -65,13 +71,14 @@ typedef struct #define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE ) #define GTHDRSIZE ( VARHDRSZ + sizeof(int32) ) -#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) ) +#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) ) #define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) ) +#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE ) #define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) ) #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) ) -static int32 sizebitvec(BITVECP sign); +static int32 sizebitvec(BITVECP sign, int siglen); Datum gtsvectorin(PG_FUNCTION_ARGS) @@ -102,9 +109,10 @@ gtsvectorout(PG_FUNCTION_ARGS) sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key)); else { - int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key)); + int siglen = GETSIGLEN(key); + int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen); - sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue); + sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue); } PG_FREE_IF_COPY(key, 0); @@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l) } static void -makesign(BITVECP sign, SignTSVector *a) +makesign(BITVECP sign, SignTSVector *a, int siglen) { int32 k, len = ARRNELEM(a); int32 *ptr = GETARR(a); - MemSet((void *) sign, 0, sizeof(BITVEC)); + MemSet((void *) sign, 0, siglen); for (k = 0; k < len; k++) - HASH(sign, ptr[k]); + HASH(sign, ptr[k], siglen); +} + +static SignTSVector * +gtsvector_alloc(int flag, int len, BITVECP sign) +{ + int size = CALCGTSIZE(flag, len); + SignTSVector *res = palloc(size); + + SET_VARSIZE(res, size); + res->flag = flag; + + if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign) + memcpy(GETSIGN(res), sign, len); + + return res; } + Datum gtsvector_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen; GISTENTRY *retval = entry; if (entry->leafkey) { /* tsvector */ - SignTSVector *res; TSVector val = DatumGetTSVector(entry->key); + SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL); int32 len; int32 *arr; WordEntry *ptr = ARRPTR(val); char *words = STRPTR(val); - len = CALCGTSIZE(ARRKEY, val->size); - res = (SignTSVector *) palloc(len); - SET_VARSIZE(res, len); - res->flag = ARRKEY; arr = GETARR(res); len = val->size; while (len--) @@ -208,13 +229,9 @@ gtsvector_compress(PG_FUNCTION_ARGS) /* make signature, if array is too long */ if (VARSIZE(res) > TOAST_INDEX_TARGET) { - SignTSVector *ressign; + SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL); - len = CALCGTSIZE(SIGNKEY, 0); - ressign = (SignTSVector *) palloc(len); - SET_VARSIZE(ressign, len); - ressign->flag = SIGNKEY; - makesign(GETSIGN(ressign), res); + makesign(GETSIGN(ressign), res, siglen); res = ressign; } @@ -226,22 +243,17 @@ gtsvector_compress(PG_FUNCTION_ARGS) else if (ISSIGNKEY(DatumGetPointer(entry->key)) && !ISALLTRUE(DatumGetPointer(entry->key))) { - int32 i, - len; + int32 i; SignTSVector *res; BITVECP sign = GETSIGN(DatumGetPointer(entry->key)); - LOOPBYTE + LOOPBYTE(siglen) { if ((sign[i] & 0xff) != 0xff) PG_RETURN_POINTER(retval); } - len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0); - res = (SignTSVector *) palloc(len); - SET_VARSIZE(res, len); - res->flag = SIGNKEY | ALLISTRUE; - + res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign); retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, @@ -315,12 +327,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data) static bool checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data) { + void *key = (SignTSVector *) checkval; + /* * we are not able to find a prefix in signature tree */ if (val->prefix) return true; - return GETBIT(checkval, HASHVAL(val->valcrc)); + return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key))); } Datum @@ -347,7 +361,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS) /* since signature is lossy, cannot specify CALC_NOT here */ PG_RETURN_BOOL(TS_execute(GETQUERY(query), - (void *) GETSIGN(key), + key, TS_EXEC_PHRASE_NO_POS, checkcondition_bit)); } @@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS) } static int32 -unionkey(BITVECP sbase, SignTSVector *add) +unionkey(BITVECP sbase, SignTSVector *add, int siglen) { int32 i; @@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add) if (ISALLTRUE(add)) return 1; - LOOPBYTE + Assert(GETSIGLEN(add) == siglen); + + LOOPBYTE(siglen) sbase[i] |= sadd[i]; } else @@ -384,7 +400,7 @@ unionkey(BITVECP sbase, SignTSVector *add) int32 *ptr = GETARR(add); for (i = 0; i < ARRNELEM(add); i++) - HASH(sbase, ptr[i]); + HASH(sbase, ptr[i], siglen); } return 0; } @@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); int *size = (int *) PG_GETARG_POINTER(1); - BITVEC base; - int32 i, - len; - int32 flag = 0; - SignTSVector *result; + int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen; + SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL); + BITVECP base = GETSIGN(result); + int32 i; + + memset(base, 0, siglen); - MemSet((void *) base, 0, sizeof(BITVEC)); for (i = 0; i < entryvec->n; i++) { - if (unionkey(base, GETENTRY(entryvec, i))) + if (unionkey(base, GETENTRY(entryvec, i), siglen)) { - flag = ALLISTRUE; + result->flag |= ALLISTRUE; + SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen)); break; } } - flag |= SIGNKEY; - len = CALCGTSIZE(flag, 0); - result = (SignTSVector *) palloc(len); - *size = len; - SET_VARSIZE(result, len); - result->flag = flag; - if (!ISALLTRUE(result)) - memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC)); + *size = VARSIZE(result); PG_RETURN_POINTER(result); } @@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS) SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0); SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); + int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen; if (ISSIGNKEY(a)) { /* then b also ISSIGNKEY */ @@ -444,8 +455,10 @@ gtsvector_same(PG_FUNCTION_ARGS) BITVECP sa = GETSIGN(a), sb = GETSIGN(b); + Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen); + *result = true; - LOOPBYTE + LOOPBYTE(siglen) { if (sa[i] != sb[i]) { @@ -482,19 +495,19 @@ gtsvector_same(PG_FUNCTION_ARGS) } static int32 -sizebitvec(BITVECP sign) +sizebitvec(BITVECP sign, int siglen) { - return pg_popcount(sign, SIGLEN); + return pg_popcount(sign, siglen); } static int -hemdistsign(BITVECP a, BITVECP b) +hemdistsign(BITVECP a, BITVECP b, int siglen) { int i, diff, dist = 0; - LOOPBYTE + LOOPBYTE(siglen) { diff = (unsigned char) (a[i] ^ b[i]); /* Using the popcount functions here isn't likely to win */ @@ -506,17 +519,22 @@ hemdistsign(BITVECP a, BITVECP b) static int hemdist(SignTSVector *a, SignTSVector *b) { + int siglena = GETSIGLEN(a); + int siglenb = GETSIGLEN(b); + if (ISALLTRUE(a)) { if (ISALLTRUE(b)) return 0; else - return SIGLENBIT - sizebitvec(GETSIGN(b)); + return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb); } else if (ISALLTRUE(b)) - return SIGLENBIT - sizebitvec(GETSIGN(a)); + return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena); - return hemdistsign(GETSIGN(a), GETSIGN(b)); + Assert(siglena == siglenb); + + return hemdistsign(GETSIGN(a), GETSIGN(b), siglena); } Datum @@ -525,6 +543,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS) GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */ GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); float *penalty = (float *) PG_GETARG_POINTER(2); + int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen; SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key); SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key); BITVECP orig = GETSIGN(origval); @@ -533,14 +552,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS) if (ISARRKEY(newval)) { - BITVEC sign; + BITVECP sign = palloc(siglen); - makesign(sign, newval); + makesign(sign, newval, siglen); if (ISALLTRUE(origval)) - *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1); + { + int siglenbit = SIGLENBIT(siglen); + + *penalty = + (float) (siglenbit - sizebitvec(sign, siglen)) / + (float) (siglenbit + 1); + } else - *penalty = hemdistsign(sign, orig); + *penalty = hemdistsign(sign, orig, siglen); + + pfree(sign); } else *penalty = hemdist(origval, newval); @@ -550,19 +577,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS) typedef struct { bool allistrue; - BITVEC sign; + BITVECP sign; } CACHESIGN; static void -fillcache(CACHESIGN *item, SignTSVector *key) +fillcache(CACHESIGN *item, SignTSVector *key, int siglen) { item->allistrue = false; if (ISARRKEY(key)) - makesign(item->sign, key); + makesign(item->sign, key, siglen); else if (ISALLTRUE(key)) item->allistrue = true; else - memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC)); + memcpy((void *) item->sign, (void *) GETSIGN(key), siglen); } #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) ) @@ -586,19 +613,19 @@ comparecost(const void *va, const void *vb) static int -hemdistcache(CACHESIGN *a, CACHESIGN *b) +hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen) { if (a->allistrue) { if (b->allistrue) return 0; else - return SIGLENBIT - sizebitvec(b->sign); + return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen); } else if (b->allistrue) - return SIGLENBIT - sizebitvec(a->sign); + return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen); - return hemdistsign(a->sign, b->sign); + return hemdistsign(a->sign, b->sign, siglen); } Datum @@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen; OffsetNumber k, j; SignTSVector *datum_l, @@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) BITVECP ptr; int i; CACHESIGN *cache; + char *cache_sign; SPLITCOST *costvector; maxoff = entryvec->n - 2; @@ -633,16 +662,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) v->spl_right = (OffsetNumber *) palloc(nbytes); cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2)); - fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber)); + cache_sign = palloc(siglen * (maxoff + 2)); + + for (j = 0; j < maxoff + 2; j++) + cache[j].sign = &cache_sign[siglen * j]; + + fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber), + siglen); for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) { for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) { if (k == FirstOffsetNumber) - fillcache(&cache[j], GETENTRY(entryvec, j)); + fillcache(&cache[j], GETENTRY(entryvec, j), siglen); - size_waste = hemdistcache(&(cache[j]), &(cache[k])); + size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen); if (size_waste > waste) { waste = size_waste; @@ -664,44 +699,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) } /* form initial .. */ - if (cache[seed_1].allistrue) - { - datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); - SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); - datum_l->flag = SIGNKEY | ALLISTRUE; - } - else - { - datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0)); - SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0)); - datum_l->flag = SIGNKEY; - memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC)); - } - if (cache[seed_2].allistrue) - { - datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); - SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); - datum_r->flag = SIGNKEY | ALLISTRUE; - } - else - { - datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0)); - SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0)); - datum_r->flag = SIGNKEY; - memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC)); - } - + datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0), + siglen, cache[seed_1].sign); + datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0), + siglen, cache[seed_2].sign); union_l = GETSIGN(datum_l); union_r = GETSIGN(datum_r); maxoff = OffsetNumberNext(maxoff); - fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff)); + fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen); /* sort before ... */ costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff); for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) { costvector[j - 1].pos = j; - size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j])); - size_beta = hemdistcache(&(cache[seed_2]), &(cache[j])); + size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen); + size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen); costvector[j - 1].cost = Abs(size_alpha - size_beta); } qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost); @@ -727,36 +739,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) if (ISALLTRUE(datum_l) && cache[j].allistrue) size_alpha = 0; else - size_alpha = SIGLENBIT - sizebitvec( - (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign) - ); + size_alpha = SIGLENBIT(siglen) - + sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen); } else - size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l)); + size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen); if (ISALLTRUE(datum_r) || cache[j].allistrue) { if (ISALLTRUE(datum_r) && cache[j].allistrue) size_beta = 0; else - size_beta = SIGLENBIT - sizebitvec( - (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign) - ); + size_beta = SIGLENBIT(siglen) - + sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen); } else - size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r)); + size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen); if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) { if (ISALLTRUE(datum_l) || cache[j].allistrue) { if (!ISALLTRUE(datum_l)) - MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC)); + MemSet((void *) GETSIGN(datum_l), 0xff, siglen); } else { ptr = cache[j].sign; - LOOPBYTE + LOOPBYTE(siglen) union_l[i] |= ptr[i]; } *left++ = j; @@ -767,12 +777,12 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) if (ISALLTRUE(datum_r) || cache[j].allistrue) { if (!ISALLTRUE(datum_r)) - MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC)); + MemSet((void *) GETSIGN(datum_r), 0xff, siglen); } else { ptr = cache[j].sign; - LOOPBYTE + LOOPBYTE(siglen) union_r[i] |= ptr[i]; } *right++ = j; @@ -799,3 +809,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS) { return gtsvector_consistent(fcinfo); } + +Datum +gtsvector_options(PG_FUNCTION_ARGS) +{ + Datum raw_options = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + relopt_int siglen = + { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT }, + SIGLEN_DEFAULT, 1, SIGLEN_MAX }; + relopt_gen *optgen[] = { &siglen.gen }; + int offsets[] = { offsetof(GistTsVectorOptions, siglen) }; + GistTsVectorOptions *options = + parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1, + sizeof(GistTsVectorOptions), validate); + + PG_RETURN_POINTER(options); +} diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 020b7413cc..5ceee11ab1 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -458,7 +458,7 @@ amproc => 'gist_circle_distance' }, { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector', amprocrighttype => 'tsvector', amprocnum => '1', - amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' }, + amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' }, { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector', amprocrighttype => 'tsvector', amprocnum => '2', amproc => 'gtsvector_union' }, @@ -476,6 +476,9 @@ amproc => 'gtsvector_picksplit' }, { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector', amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' }, +{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector', + amprocrighttype => 'tsvector', amprocnum => '10', + amproc => 'gtsvector_options' }, { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery', amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' }, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..82b51fc1bb 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8569,30 +8569,35 @@ { oid => '3648', descr => 'GiST tsvector support', proname => 'gtsvector_compress', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'gtsvector_compress' }, + proargtypes => 'internal internal', prosrc => 'gtsvector_compress' }, { oid => '3649', descr => 'GiST tsvector support', proname => 'gtsvector_decompress', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'gtsvector_decompress' }, + proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' }, { oid => '3650', descr => 'GiST tsvector support', proname => 'gtsvector_picksplit', prorettype => 'internal', - proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' }, + proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' }, { oid => '3651', descr => 'GiST tsvector support', proname => 'gtsvector_union', prorettype => 'gtsvector', - proargtypes => 'internal internal', prosrc => 'gtsvector_union' }, + proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' }, { oid => '3652', descr => 'GiST tsvector support', proname => 'gtsvector_same', prorettype => 'internal', - proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' }, + proargtypes => 'gtsvector gtsvector internal internal', + prosrc => 'gtsvector_same' }, { oid => '3653', descr => 'GiST tsvector support', proname => 'gtsvector_penalty', prorettype => 'internal', - proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' }, + proargtypes => 'internal internal internal internal', + prosrc => 'gtsvector_penalty' }, { oid => '3654', descr => 'GiST tsvector support', proname => 'gtsvector_consistent', prorettype => 'bool', - proargtypes => 'internal tsvector int2 oid internal', + proargtypes => 'internal tsvector int2 oid internal internal', prosrc => 'gtsvector_consistent' }, { oid => '3790', descr => 'GiST tsvector support (obsolete)', proname => 'gtsvector_consistent', prorettype => 'bool', proargtypes => 'internal gtsvector int4 oid internal', prosrc => 'gtsvector_consistent_oldsig' }, +{ oid => '3998', descr => 'GiST tsvector support', + proname => 'gtsvector_options', prorettype => 'internal', + proargtypes => 'internal bool', prosrc => 'gtsvector_options' }, { oid => '3656', descr => 'GIN tsvector support', proname => 'gin_extract_tsvector', prorettype => 'internal', diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out index 6f61acc1ed..a1873dc722 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; 508 (1 row) +-- Test siglen parameter of GiST tsvector_ops +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1)); +ERROR: unrecognized parameter "foo" +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0)); +ERROR: value 0 out of bounds for option "siglen" +DETAIL: Valid values are between "1" and "484". +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485)); +ERROR: value 485 out of bounds for option "siglen" +DETAIL: Valid values are between "1" and "484". +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar')); +ERROR: unrecognized parameter "foo" +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200)); +ERROR: parameter "siglen" specified more than once +CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1)); +\d test_tsvector + Table "public.test_tsvector" + Column | Type | Collation | Nullable | Default +--------+----------+-----------+----------+--------- + t | text | | | + a | tsvector | | | +Indexes: + "wowidx" gist (a) + "wowidx2" gist (a tsvector_ops (siglen='1')) + +DROP INDEX wowidx; +EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + QUERY PLAN +------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_tsvector + Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery) + -> Bitmap Index Scan on wowidx2 + Index Cond: (a @@ '''wr'' | ''qh'''::tsquery) +(5 rows) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + count +------- + 158 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; + count +------- + 17 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; + count +------- + 6 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; + count +------- + 98 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; + count +------- + 23 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; + count +------- + 39 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; + count +------- + 494 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); + count +------- + 158 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; + count +------- + 0 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; + count +------- + 508 +(1 row) + +DROP INDEX wowidx2; +CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484)); +\d test_tsvector + Table "public.test_tsvector" + Column | Type | Collation | Nullable | Default +--------+----------+-----------+----------+--------- + t | text | | | + a | tsvector | | | +Indexes: + "wowidx" gist (a tsvector_ops (siglen='484')) + +EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + QUERY PLAN +------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_tsvector + Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery) + -> Bitmap Index Scan on wowidx + Index Cond: (a @@ '''wr'' | ''qh'''::tsquery) +(5 rows) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + count +------- + 158 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; + count +------- + 17 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; + count +------- + 6 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; + count +------- + 98 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; + count +------- + 23 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; + count +------- + 39 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; + count +------- + 494 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); + count +------- + 158 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; + count +------- + 0 +(1 row) + +SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; + count +------- + 508 +(1 row) + RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql index 637bfb3012..9aed780d0c 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; +-- Test siglen parameter of GiST tsvector_ops +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1)); +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0)); +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485)); +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar')); +CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200)); + +CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1)); + +\d test_tsvector + +DROP INDEX wowidx; + +EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; +SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); +SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; +SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; + +DROP INDEX wowidx2; + +CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484)); + +\d test_tsvector + +EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; + +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; +SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; +SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); +SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; +SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; + RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- 2.20.1
0005-Add-opclass-parameters-to-BRIN-20190611.patch (text/plain, 6.6K) download From 20a6e4eb0c0223feaebd996371a006698bfa1d01 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Mon, 10 Jun 2019 18:49:52 +0200 Subject: [PATCH 05/10] Add opclass parameters to BRIN --- src/backend/access/brin/brin.c | 10 ++++++++++ src/backend/access/brin/brin_validate.c | 16 ++++++++++------ src/include/access/brin.h | 2 +- src/include/access/brin_internal.h | 3 +++ src/include/catalog/pg_proc.dat | 12 ++++++------ 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ae7b729edd..63f2d7990c 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -102,6 +102,7 @@ brinhandler(PG_FUNCTION_ARGS) amroutine->amcanparallel = false; amroutine->amcaninclude = false; amroutine->amkeytype = InvalidOid; + amroutine->amopclassoptions = brinopclassoptions; amroutine->ambuild = brinbuild; amroutine->ambuildempty = brinbuildempty; @@ -518,6 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) PointerGetDatum(bval), PointerGetDatum(key)); addrange = DatumGetBool(add); + if (!addrange) break; } @@ -845,6 +847,14 @@ brinoptions(Datum reloptions, bool validate) return (bytea *) rdopts; } +bytea * +brinopclassoptions(Relation index, AttrNumber attnum, Datum attoptions, + bool validate) +{ + return index_opclass_options_generic(index, attnum, BRIN_OPCLASSOPT_PROC, + attoptions, validate); +} + /* * SQL-callable function to scan through an index and summarize all ranges * that are not currently summarized. diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index 012933833b..4222a1781f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -93,18 +93,22 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_ADDVALUE: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 4, 4, INTERNALOID, INTERNALOID, - INTERNALOID, INTERNALOID); + 4, 5, INTERNALOID, INTERNALOID, + INTERNALOID, INTERNALOID, INTERNALOID); break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INTERNALOID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INTERNALOID); + break; + case BRIN_OPCLASSOPT_PROC: + ok = check_amproc_signature(procform->amproc, INTERNALOID, false, + 2, 2, INTERNALOID, BOOLOID); break; default: /* Complain if it's not a valid optional proc number */ diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 612721baf3..8271404332 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -37,6 +37,7 @@ typedef struct BrinStatsData #define BRIN_DEFAULT_PAGES_PER_RANGE 128 + #define BrinGetPagesPerRange(relation) \ ((relation)->rd_options ? \ ((BrinOptions *) (relation)->rd_options)->pagesPerRange : \ @@ -46,7 +47,6 @@ typedef struct BrinStatsData ((BrinOptions *) (relation)->rd_options)->autosummarize : \ false) - extern void brinGetStats(Relation index, BrinStatsData *stats); #endif /* BRIN_H */ diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index b1c9199946..0532094e28 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -68,6 +68,7 @@ typedef struct BrinDesc #define BRIN_PROCNUM_ADDVALUE 2 #define BRIN_PROCNUM_CONSISTENT 3 #define BRIN_PROCNUM_UNION 4 +#define BRIN_OPCLASSOPT_PROC 5 /* optional */ #define BRIN_MANDATORY_NPROCS 4 /* procedure numbers up to 10 are reserved for BRIN future expansion */ #define BRIN_FIRST_OPTIONAL_PROCNUM 11 @@ -103,6 +104,8 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info, extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern bytea *brinoptions(Datum reloptions, bool validate); +extern bytea *brinopclassoptions(Relation index, AttrNumber colno, + Datum attoptions, bool validate); /* brin_validate.c */ extern bool brinvalidate(Oid opclassoid); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 82b51fc1bb..bc3d08caec 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7887,15 +7887,15 @@ proargtypes => 'internal', prosrc => 'brin_minmax_opcinfo' }, { oid => '3384', descr => 'BRIN minmax support', proname => 'brin_minmax_add_value', prorettype => 'bool', - proargtypes => 'internal internal internal internal', + proargtypes => 'internal internal internal internal internal', prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal internal', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', - proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, + proargtypes => 'internal internal internal internal', prosrc => 'brin_minmax_union' }, # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', @@ -7903,15 +7903,15 @@ proargtypes => 'internal', prosrc => 'brin_inclusion_opcinfo' }, { oid => '4106', descr => 'BRIN inclusion support', proname => 'brin_inclusion_add_value', prorettype => 'bool', - proargtypes => 'internal internal internal internal', + proargtypes => 'internal internal internal internal internal', prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal internal', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal internal', prosrc => 'brin_inclusion_union' }, # userlock replacements -- 2.20.1
0006-Pass-all-keys-to-BRIN-consistent-function-a-20190611.patch (text/plain, 20.9K) download From be7b3bb9ce6c0ab3937c074cd5497599c8a42590 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:04:50 +0200 Subject: [PATCH 06/10] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 125 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 292 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 63f2d7990c..31d09bc88e 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -383,6 +383,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -404,6 +407,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -464,7 +514,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -474,34 +524,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -513,12 +548,42 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } if (!addrange) break; diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 86788024ef..f9c2918031 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -252,53 +254,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -318,49 +370,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -379,7 +431,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -400,9 +452,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -419,12 +471,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -454,9 +506,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -464,30 +516,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index ad0d18ed39..2266fef2f9 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -31,6 +31,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -147,47 +149,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -230,7 +284,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index 4222a1781f..257096c615 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -98,8 +98,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 4, INTERNALOID, INTERNALOID, - INTERNALOID, INTERNALOID); + 3, 5, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID, INTERNALOID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bc3d08caec..3fb4073367 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7891,7 +7891,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal internal', + proargtypes => 'internal internal internal int4 internal', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -7907,7 +7907,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal internal', + proargtypes => 'internal internal internal int4 internal', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.20.1
0007-Move-IS-NOT-NULL-checks-to-bringetbitmap-20190611.patch (text/plain, 9.9K) download From e040ce7299b2bf691b2c4ed4dcc682a754b4d8e8 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 07/10] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 31d09bc88e..cfeb4a9c72 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -383,8 +383,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -409,10 +411,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -434,14 +439,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -449,9 +452,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -538,6 +555,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index f9c2918031..b7b3f9937e 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -259,63 +259,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -327,9 +270,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 2266fef2f9..fc5e61b370 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -154,63 +154,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -218,9 +161,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.20.1
0008-Move-processing-of-NULLs-from-BRIN-support--20190611.patch (text/plain, 16.1K) download From 9edb50e7a368f73da94e205b8ace83dc6a245fe1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:06:43 +0200 Subject: [PATCH 08/10] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index cfeb4a9c72..67f42ed5d1 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -33,6 +33,7 @@ #include "storage/bufmgr.h" #include "storage/freespace.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -75,7 +76,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -172,7 +175,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -236,31 +238,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -353,6 +331,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -556,69 +535,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -766,7 +707,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(&htup->t_self); @@ -795,25 +735,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1614,6 +1537,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1667,3 +1623,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index b7b3f9937e..9cfd978507 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -504,37 +494,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index fc5e61b370..b8cff2e567 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -49,6 +49,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -70,7 +71,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -78,18 +79,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -246,34 +236,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 0532094e28..21a79803b3 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.20.1
0009-BRIN-bloom-indexes-20190611.patch (text/plain, 123.6K) download From c6e551147563a092a4fd003d4e497cfaf4dcced1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:09:09 +0200 Subject: [PATCH 09/10] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 215 ++++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 2 +- src/backend/access/brin/brin.c | 22 +- src/backend/access/brin/brin_bloom.c | 858 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 +++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 ++++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 +++++++++++ 17 files changed, 2715 insertions(+), 8 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index da0c911153..b73f0b7d2b 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -129,6 +129,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> @@ -180,6 +187,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>|&amp;&gt;</literal> </entry> </row> + <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> @@ -191,6 +205,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> @@ -202,6 +223,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> @@ -213,6 +241,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> @@ -224,6 +259,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> @@ -235,6 +277,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> @@ -258,6 +307,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&lt;&lt;</literal> </entry> </row> + <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> @@ -269,6 +325,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> @@ -280,6 +343,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -291,6 +361,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -302,6 +379,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> @@ -313,6 +397,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> @@ -324,6 +415,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> @@ -335,6 +433,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> @@ -366,6 +471,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;=</literal> </entry> </row> + <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> @@ -377,6 +489,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> @@ -388,6 +507,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> @@ -410,6 +536,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> @@ -421,6 +554,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -432,6 +572,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -443,6 +590,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -454,6 +608,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> @@ -803,6 +964,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 61401f3645..05b2b01614 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -523,6 +523,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 5aef925ed4..a76d927ee0 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,6 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 67f42ed5d1..8878bf10f7 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -53,6 +53,7 @@ typedef struct BrinBuildState BrinRevmap *bs_rmAccess; BrinDesc *bs_bdesc; BrinMemTuple *bs_dtuple; + bytea **opclassOptions; } BrinBuildState; /* @@ -77,7 +78,8 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, - BrinMemTuple *dtup, Datum *values, bool *nulls); + BrinMemTuple *dtup, Datum *values, bool *nulls, + bytea **options); static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* @@ -238,7 +240,8 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); + /* FIXME use correct opclass options */ + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls, NULL); if (!need_insert) { @@ -736,7 +739,7 @@ brinbuildCallback(Relation index, /* Accumulate the current tuple into the running state */ (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, - values, isnull); + values, isnull, state->opclassOptions); } /* @@ -1218,6 +1221,7 @@ initialize_brin_buildstate(Relation idxRel, BrinRevmap *revmap, state->bs_rmAccess = revmap; state->bs_bdesc = brin_build_desc(idxRel); state->bs_dtuple = brin_new_memtuple(state->bs_bdesc); + state->opclassOptions = RelationGetParsedOpclassOptions(idxRel); brin_memtuple_initialize(state->bs_dtuple, state->bs_bdesc); @@ -1250,6 +1254,7 @@ terminate_brin_buildstate(BrinBuildState *state) brin_free_desc(state->bs_bdesc); pfree(state->bs_dtuple); + pfree(state->opclassOptions); pfree(state); } @@ -1626,7 +1631,7 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, - Datum *values, bool *nulls) + Datum *values, bool *nulls, bytea **options) { int keyno; bool modified = false; @@ -1643,6 +1648,7 @@ add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, Datum result; BrinValues *bval; FmgrInfo *addValue; + bytea *opts; bval = &dtup->bt_columns[keyno]; @@ -1661,14 +1667,18 @@ add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, continue; } + opts = (options) ? options[keyno] : NULL; + + addValue = index_getprocinfo(idxRel, keyno + 1, BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, + result = FunctionCall5Coll(addValue, idxRel->rd_indcollation[keyno], PointerGetDatum(bdesc), PointerGetDatum(bval), values[keyno], - nulls[keyno]); + nulls[keyno], + PointerGetDatum(opts)); /* if that returned true, we need to insert the updated tuple */ modified |= DatumGetBool(result); } diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..ef026a72d5 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,858 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 4 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +#define BLOOM_PHASE_SORTED 1 +#define BLOOM_PHASE_HASH 2 + +/* how many hashes to accumulate before hashing */ +#define BLOOM_MAX_UNSORTED 32 +#define BLOOM_GROW_BYTES 32 + +/* + * Storage type for BRIN's reloptions + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* default values for bloom-specific reloptions */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 128 +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Maybe instead of explicitly limiting the number of unsorted values + * by BLOOM_MAX_UNSORTED, we should cap them by (filter size / 4B), i.e. + * allow up to the whole filter size. + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* global bloom filter parameters */ + uint32 phase; /* phase (initially SORTED, then HASH) */ + uint32 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (optimal) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* fields used only in the EXACT phase */ + uint32 nvalues; /* number of hashes stored (sorted + extra) */ + uint32 nsorted; /* number of uint32 hashes in sorted part */ + + /* bitmap of the bloom filter */ + char bitmap[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + /* https://en.wikipedia.org/wiki/Bloom_filter */ + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + */ + len = Max(offsetof(BloomFilter, bitmap), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->phase = BLOOM_PHASE_SORTED; + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * XXX Actually, we don't need to do repalloc - we just need to set the + * varlena header length! + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(filter->phase == BLOOM_PHASE_SORTED); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) palloc(filter->nvalues * sizeof(uint32)); + + /* copy the data, then reset the bitmap */ + memcpy(values, filter->bitmap, filter->nvalues * sizeof(uint32)); + memset(filter->bitmap, 0, filter->nvalues * sizeof(uint32)); + + /* FIXME optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if a new value, keep it */ + if (values[i] != values[i-1]) + { + values[nvalues] = values[i]; + nvalues++; + } + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + memcpy(filter->bitmap, values, nvalues * sizeof(uint32)); + + pfree(values); +} + +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter); + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter,bitmap) + (filter->nvalues+1) * sizeof(uint32); + + if (len < need) + { + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts anyway. + * + * XXX Zero the newly allocated part. Maybe not really needed? + */ + filter = (BloomFilter *) repalloc(filter, len + BLOOM_GROW_BYTES); + memset((char *)filter + len, 0, BLOOM_GROW_BYTES); + SET_VARSIZE(filter, len + BLOOM_GROW_BYTES); + } + + /* copy the data into the bitmap */ + memcpy(&filter->bitmap[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32)DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + { + filter->bitmap[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, bitmap) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->phase = BLOOM_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->bitmap; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (filter->phase == BLOOM_PHASE_SORTED) + { + int i; + uint32 *values = (uint32 *)filter->bitmap; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + if (value == values[i]) + return true; + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(filter->phase == BLOOM_PHASE_HASH); + + big_h = ((uint32)DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->bitmap[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store a the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct for the column(s) and + * compute the expected number of distinct values in a range. But that + * may be tricky due to data being sorted in various ways, so it seems + * better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unnecessarily small bloom filters. + * We never use less than 128, because we would not save much space + * anyway. MaxHeapTuplesPerPage is ~290, so this is still lower. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GETARG_POINTER(4); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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); + // BloomOptions *opts = (BloomOptions *) PG_GETARG_POINTER(4); + Oid colloid = PG_GET_COLLATION(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + // BloomOptions *opts = (BloomOptions *) PG_GETARG_POINTER(3); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (filter_b->phase == BLOOM_PHASE_SORTED) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->bitmap; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (filter_a->phase == BLOOM_PHASE_SORTED) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->bitmap; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (filter_a->phase == BLOOM_PHASE_HASH) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + for (i = 0; i < nbytes; i++) + filter_a->bitmap[i] |= filter_b->bitmap[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + Datum raw_options = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + relopt_real opts[] = { + { + {"n_distinct_per_range", "xxx", 0, 0, 20, RELOPT_TYPE_REAL }, + -0.1, -1.0, INT_MAX + }, + { + {"false_positive_rate", "xxx", 0, 0, 19, RELOPT_TYPE_REAL }, + 0.01, 0.001, 1.0 + } + }; + + relopt_gen *optgen[] = { &opts[0].gen, &opts[1].gen }; + int offsets[] = { offsetof(BloomOptions, nDistinctPerRange), + offsetof(BloomOptions, falsePositiveRate)}; + + BloomOptions *options = + parseAndFillLocalRelOptions(raw_options, optgen, offsets, 2, + sizeof(BloomOptions), validate); + + PG_RETURN_POINTER(options); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 8271404332..37cf154e91 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -23,6 +23,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 21a79803b3..633e1ec90e 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index cf63eb7d54..90ae219598 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1675,6 +1675,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1692,6 +1697,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1709,6 +1719,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1855,6 +1870,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1872,6 +1925,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1889,6 +1947,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1972,6 +2035,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -1989,6 +2066,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2006,6 +2088,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2023,6 +2110,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2060,6 +2152,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2077,6 +2174,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2223,6 +2325,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2240,6 +2380,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2257,6 +2402,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2308,6 +2458,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2325,6 +2480,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2386,6 +2546,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 5ceee11ab1..c7199f0a91 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -697,6 +697,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -710,6 +728,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -723,6 +759,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -824,6 +878,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -837,6 +943,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -849,6 +973,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -911,6 +1052,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -925,6 +1105,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -939,6 +1139,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -952,6 +1172,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -986,6 +1224,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -999,6 +1257,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1106,6 +1382,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1120,6 +1452,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1134,6 +1486,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1174,6 +1546,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1187,6 +1579,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1222,6 +1632,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index fdfea85efe..ebc35fd98c 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -250,67 +250,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -319,18 +379,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 41e40d657a..fd3d659b68 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -176,50 +176,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '5024', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '5027', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '5045', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '5042', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '5038', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '5022', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '5048', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '5023', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '5049', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '5050', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '5033', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '5034', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '5035', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '5036', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '5037', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '5041', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '5046', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '5047', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 3fb4073367..0a1a83807b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7914,6 +7914,26 @@ proargtypes => 'internal internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '5039', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '5040', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '5043', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4 internal', + prosrc => 'brin_bloom_consistent' }, +{ oid => '5044', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '6016', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'internal', + proargtypes => 'internal bool', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'u', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 85af36ee5b..400f333ef2 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1924,6 +1924,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -1987,7 +1988,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index f23fe8d870..38a18660da 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index ca200eb599..56fb53d053 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -105,6 +105,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.20.1
0010-BRIN-multi-range-minmax-indexes-20190611.patch (text/plain, 198.1K) download From 47999d4fe405860de81087b05931e5a617f66289 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:10:50 +0200 Subject: [PATCH 10/10] BRIN multi-range minmax indexes --- doc/src/sgml/brin.sgml | 212 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 3 +- src/backend/access/brin/brin_minmax_multi.c | 2187 +++++++++++++++++++ src/backend/access/index/indexam.c | 3 +- src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 545 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 30 +- src/include/catalog/pg_proc.dat | 65 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 ++++ 17 files changed, 4504 insertions(+), 16 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index b73f0b7d2b..9cdcb4652d 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -259,6 +259,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_minmax_multi_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>float8_bloom_ops</literal></entry> <entry><type>double precision</type></entry> @@ -277,6 +288,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_minmax_multi_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>inet_bloom_ops</literal></entry> <entry><type>inet</type></entry> @@ -295,6 +317,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_minmax_multi_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>network_inclusion_ops</literal></entry> <entry><type>inet</type></entry> @@ -343,6 +376,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_minmax_multi_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr_bloom_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -361,6 +405,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr8_bloom_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -379,6 +434,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>name_bloom_ops</literal></entry> <entry><type>name</type></entry> @@ -433,6 +499,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>oid_bloom_ops</literal></entry> <entry><type>oid</type></entry> @@ -554,6 +631,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timestamptz_bloom_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -572,6 +660,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>time_bloom_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -590,6 +689,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_minmax_multi_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timetz_bloom_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -608,6 +718,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_minmax_multi_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>uuid_bloom_ops</literal></entry> <entry><type>uuid</type></entry> @@ -626,6 +747,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_minmax_multi_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> </tbody> </tgroup> </table> @@ -720,13 +852,13 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions using + them are shipped for in-core data types as appropriate. Additional operator + classes can be defined by the user for other data types using equivalent + definitions, without having to write any source code; appropriate catalog + entries being declared is enough. Note that assumptions about the semantics + of operator strategies are embedded in the support functions' source code. </para> <para> @@ -795,6 +927,72 @@ typedef struct BrinOpcInfo </tgroup> </table> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> To write an operator class for a complex data type which has values included within another type, it's possible to use the inclusion support diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 05b2b01614..d1f3ab1102 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -554,6 +554,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index a76d927ee0..c87c796472 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,7 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o + brin_minmax.o brin_inclusion.o brin_validate.o brin_bloom.o \ + brin_minmax_multi.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..d4d97823ce --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2187 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + /* (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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (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; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + FmgrInfo *cmpFn; + Datum *values; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(bdesc->bd_index); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GETARG_POINTER(4); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GETARG_POINTER(4); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GETARG_POINTER(3); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + Datum raw_options = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + relopt_int opts[] = { + { + {"values_per_range", "xxx", 0, 0, 16, RELOPT_TYPE_INT }, + 0, 16, 256 + } + }; + + relopt_gen *optgen[] = { &opts[0].gen }; + int offsets[] = { offsetof(MinMaxOptions, valuesPerRange)}; + + MinMaxOptions *options = + parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1, + sizeof(MinMaxOptions), validate); + + PG_RETURN_POINTER(options); +} diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 6cc76914de..8e74684050 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -974,10 +974,11 @@ index_opclass_options_generic(Relation indrel, AttrNumber attnum, initStringInfo(&opclassname); get_opclass_name(opclass, InvalidOid, &opclassname); + /* XXX get_opclass_name prepends the name with a space */ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("operator class \"%s\" has no options", - opclassname.data))); + &opclassname.data[1]))); } procinfo = index_getprocinfo(indrel, attnum, procnum); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 37cf154e91..6698ec688d 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -25,6 +25,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 633e1ec90e..cc9a728815 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 6cbb0c82c7..f95caf6c1c 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -116,14 +116,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -137,7 +137,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 90ae219598..2f5b538f58 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1870,6 +1870,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1947,6 +2093,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1969,6 +2132,23 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2035,6 +2215,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2066,6 +2312,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2088,6 +2351,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2110,6 +2390,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2174,6 +2471,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2325,6 +2639,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2380,6 +2840,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2402,6 +2879,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2458,6 +2952,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2480,6 +2991,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2546,6 +3074,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index c7199f0a91..ea82f8e1e4 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -878,6 +878,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -973,6 +1119,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1002,6 +1165,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1052,6 +1232,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1105,6 +1359,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1139,6 +1413,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1172,6 +1466,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1257,6 +1571,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1382,6 +1715,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1452,6 +1949,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1486,6 +2003,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1546,6 +2083,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1577,7 +2134,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1632,6 +2210,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ebc35fd98c..32c3ed7988 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -268,18 +268,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -291,38 +300,59 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -338,36 +368,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -379,6 +427,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -388,6 +439,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -397,6 +451,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index fd3d659b68..f3e0936a67 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -176,10 +176,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '5054', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '5024', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '5087', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '5027', @@ -188,10 +192,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '5059', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '5042', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '5088', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '5038', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -209,23 +217,35 @@ { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, { oid => '5049', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, +{ oid => '5051', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '5089', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, -{ oid => '5050', +{ oid => '5091', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, +{ oid => '5052', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '5064', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '5033', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '5063', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '5034', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '5066', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '5035', @@ -236,10 +256,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '5090', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '5037', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '5061', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '5041', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -248,12 +272,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '5050', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '5046', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '5065', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '5047', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 0a1a83807b..d3d874dd52 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7897,6 +7897,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '5068', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '5069', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '5070', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4 internal', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '5071', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '6015', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'internal', + proargtypes => 'internal bool', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '5072', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '5073', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '5074', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '5075', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '5076', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '5077', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '5078', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '5079', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '5080', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '5081', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '5082', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '5083', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '5084', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '5085', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '5086', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 38a18660da..ab92ea8a13 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 56fb53d053..034030d866 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -106,6 +106,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.20.1
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-09-03 22:05                                                 ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2019-09-03 22:05 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On 2019-Jun-11, Tomas Vondra wrote:

> Attached is this patch series, rebased on top of current master and the
> opclass parameters patch [1]. I previously planned to keep those two
> efforts separate for a while, but I decided to give it a try and the
> breakage is fairly minor so I'll keep it this way - this patch has zero
> chance of getting committed with the opclass parameters patch anyway.
> 
> Aside from rebase and changes due to adopting opclass parameters, the
> patch is otherwise unchanged.

This patch series doesn't apply, but I'm leaving it alone since the
brokenness is the opclass part, for which I have pinged the other
thread.

Thanks,

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-09-14 17:30                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2019-09-14 17:30 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Tue, Sep 03, 2019 at 06:05:04PM -0400, Alvaro Herrera wrote:
>On 2019-Jun-11, Tomas Vondra wrote:
>
>> Attached is this patch series, rebased on top of current master and the
>> opclass parameters patch [1]. I previously planned to keep those two
>> efforts separate for a while, but I decided to give it a try and the
>> breakage is fairly minor so I'll keep it this way - this patch has zero
>> chance of getting committed with the opclass parameters patch anyway.
>>
>> Aside from rebase and changes due to adopting opclass parameters, the
>> patch is otherwise unchanged.
>
>This patch series doesn't apply, but I'm leaving it alone since the
>brokenness is the opclass part, for which I have pinged the other
>thread.
>

Attached is an updated version of this patch series, rebased on top of
the opclass parameter patches, shared by Nikita a couple of days ago.
There's one extra fixup patch, addressing a bug in those patches.

Firstly, while I have some comments on the opclass parameters patches
(shared in the other thread), I think that patch series is moving in the
right direction. After rebase the code is somewhat simpler and easier to
read, which is good. I'm sure there's some more work needed on the APIs
and so on, but I'm optimistic about that.

The rest of this patch series (0007-0011) is mostly unchanged. I've
fixed a couple of bugs and added some comments (particularly to the
bloom opclass), but that's about it.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 

Attachments:

  0001-Introduce-opclass-parameters.patch.gz (application/gzip, 21.0K) download

  0002-Introduce-amattoptions.patch.gz (application/gzip, 1.9K) download

  0003-Use-amattoptions-in-contrib-bloom.patch.gz (application/gzip, 2.7K) download

  0004-Use-opclass-parameters-in-GiST-tsvector_ops.patch.gz (application/gzip, 7.1K) download

  0005-Remove-pg_index.indoption.patch.gz (application/gzip, 11.1K) download

  0006-fix-amvalidate.patch.gz (application/gzip, 556B) download

  0007-Pass-all-keys-to-BRIN-consistent-function-at-once.patch.gz (application/gzip, 4.4K) download

  0008-Move-IS-NOT-NULL-checks-to-bringetbitmap.patch.gz (application/gzip, 2.7K) download

  0009-Move-processing-of-NULLs-from-BRIN-support-functions.patch.gz (application/gzip, 4.5K) download

  0010-BRIN-bloom-indexes.patch.gz (application/gzip, 21.8K) download

  0011-BRIN-multi-range-minmax-indexes.patch.gz (application/gzip, 29.4K) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-09-25 20:07                                                     ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2019-09-25 20:07 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

This patch fails to apply (or the opclass params one, maybe).  Please
update.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-09-26 19:01                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2019-09-26 19:01 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Wed, Sep 25, 2019 at 05:07:48PM -0300, Alvaro Herrera wrote:
>This patch fails to apply (or the opclass params one, maybe).  Please
>update.
>

Yeah, the opclass params patches got broken by 773df883e adding enum
reloptions. The breakage is somewhat extensive so I'll leave it up to
Nikita to fix it in [1]. Until that happens, apply the patches on
top of caba97a9d9 for review.

Thanks

[1] https://commitfest.postgresql.org/24/2183/

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2019-12-01 01:55                                                         ` Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Michael Paquier @ 2019-12-01 01:55 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
> Yeah, the opclass params patches got broken by 773df883e adding enum
> reloptions. The breakage is somewhat extensive so I'll leave it up to
> Nikita to fix it in [1]. Until that happens, apply the patches on
> top of caba97a9d9 for review.

This has been close to two months now, so I have the patch as RwF.
Feel free to update if you think that's incorrect.
--
Michael

Attachments:

  signature.asc (application/pgp-signature, 832B) download

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
@ 2020-04-02 02:29                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-04-02 02:29 UTC (permalink / raw)
  To: Michael Paquier <michael@paquier.xyz>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
>On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
>> Yeah, the opclass params patches got broken by 773df883e adding enum
>> reloptions. The breakage is somewhat extensive so I'll leave it up to
>> Nikita to fix it in [1]. Until that happens, apply the patches on
>> top of caba97a9d9 for review.
>
>This has been close to two months now, so I have the patch as RwF.
>Feel free to update if you think that's incorrect.

I see the opclass parameters patch got committed a couple days ago, so
I've rebased the patch series on top of it. The pach was marked RwF
since 2019-11, so I'll add it to the next CF.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (5 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-a-20200402.patch (text/plain, 20.8K) download From 7c2adfa6d9195812edbeb46068b48de9a8b29f95 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 1/5] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 7db3ae5ee0..f1beb2eff9 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,6 +388,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -409,6 +412,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -469,7 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -479,34 +529,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -518,12 +553,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 55ef716858..26ba8c6f98 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8039,7 +8039,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8055,7 +8055,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.21.1
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200402.patch (text/plain, 9.9K) download From 54752ea9c0461053894f27252624426c8e598b14 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 2/5] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f1beb2eff9..25ae02a999 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,8 +388,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -414,10 +416,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,14 +444,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -454,9 +457,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,6 +560,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.21.1
0003-Move-processing-of-NULLs-from-BRIN-support--20200402.patch (text/plain, 16.1K) download From 3fda6fd27c07a11f737dea4afe634afdbd03ecc6 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 3/5] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 25ae02a999..8c94252567 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -177,7 +180,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -241,31 +243,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -358,6 +336,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -561,69 +540,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -771,7 +712,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -800,25 +740,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1597,6 +1520,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1650,3 +1606,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.21.1
0004-BRIN-bloom-indexes-20200402.patch (text/plain, 126.1K) download From 2928b78a101e9bd309a18888b508636bee981c3f Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:57:59 +0200 Subject: [PATCH 4/5] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 215 +++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 ++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 +++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++++ 16 files changed, 2821 insertions(+), 1 deletion(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 176f1cfbd0..ba73966112 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -129,6 +129,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> @@ -180,6 +187,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>|&amp;&gt;</literal> </entry> </row> + <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> @@ -191,6 +205,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> @@ -202,6 +223,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> @@ -213,6 +241,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> @@ -224,6 +259,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> @@ -235,6 +277,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> @@ -258,6 +307,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&lt;&lt;</literal> </entry> </row> + <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> @@ -269,6 +325,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> @@ -280,6 +343,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -291,6 +361,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -302,6 +379,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> @@ -313,6 +397,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> @@ -324,6 +415,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> @@ -335,6 +433,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> @@ -366,6 +471,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;=</literal> </entry> </row> + <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> @@ -377,6 +489,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> @@ -388,6 +507,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> @@ -410,6 +536,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> @@ -421,6 +554,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -432,6 +572,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -443,6 +590,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -454,6 +608,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> @@ -803,6 +964,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index aaf087e2e3..2e3e837faa 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -556,6 +556,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..b119e4e264 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,980 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + Size len; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* TODO optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (values[i] != values[nvalues - 1]) + values[nvalues++] = values[i]; + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 11aaa519c8..3de5455221 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1682,6 +1682,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1699,6 +1704,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1716,6 +1726,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1862,6 +1877,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1879,6 +1932,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1896,6 +1954,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1979,6 +2042,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -1996,6 +2073,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2013,6 +2095,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2030,6 +2117,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2067,6 +2159,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2084,6 +2181,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2230,6 +2332,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2247,6 +2387,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2264,6 +2409,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2315,6 +2465,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2332,6 +2487,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2393,6 +2553,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index cef63b2a71..6d5b6fb455 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -762,6 +762,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -775,6 +793,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -788,6 +824,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -889,6 +943,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -902,6 +1008,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -914,6 +1038,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -976,6 +1117,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -990,6 +1170,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1004,6 +1204,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1017,6 +1237,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1051,6 +1289,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1064,6 +1322,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1171,6 +1447,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1185,6 +1517,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1199,6 +1551,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1239,6 +1611,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1252,6 +1644,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1287,6 +1697,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '0', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ab2f50c9eb..092511df81 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -253,67 +253,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -322,18 +382,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 26227df216..ba428ba379 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -176,50 +176,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '5024', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '5027', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '5045', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '5042', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '5038', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '5022', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '5048', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '5023', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '5049', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '5050', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '5033', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '5034', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '5035', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '5036', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '5037', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '5041', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '5046', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '5047', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 26ba8c6f98..fa908278d2 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8062,6 +8062,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '5039', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '5040', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '5043', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '5044', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '6016', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 2efd7d7ec7..cd65ae5c22 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1997,6 +1997,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2060,7 +2061,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index d2b17dd3ea..739303dbbd 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index acba391332..3e3e537951 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -104,6 +104,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.21.1
0005-BRIN-multi-range-minmax-indexes-20200402.patch (text/plain, 196.9K) download From 4b53a180ac4dc9eac7c221ee40b484f2b1c53273 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:59:25 +0200 Subject: [PATCH 5/5] BRIN multi-range minmax indexes --- doc/src/sgml/brin.sgml | 212 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2178 +++++++++++++++++++ src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 545 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 30 +- src/include/catalog/pg_proc.dat | 65 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 ++++ 16 files changed, 4492 insertions(+), 14 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index ba73966112..15335adbd1 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -259,6 +259,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_minmax_multi_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>float8_bloom_ops</literal></entry> <entry><type>double precision</type></entry> @@ -277,6 +288,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_minmax_multi_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>inet_bloom_ops</literal></entry> <entry><type>inet</type></entry> @@ -295,6 +317,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_minmax_multi_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>network_inclusion_ops</literal></entry> <entry><type>inet</type></entry> @@ -343,6 +376,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_minmax_multi_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr_bloom_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -361,6 +405,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr8_bloom_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -379,6 +434,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>name_bloom_ops</literal></entry> <entry><type>name</type></entry> @@ -433,6 +499,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>oid_bloom_ops</literal></entry> <entry><type>oid</type></entry> @@ -554,6 +631,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timestamptz_bloom_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -572,6 +660,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>time_bloom_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -590,6 +689,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_minmax_multi_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timetz_bloom_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -608,6 +718,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_minmax_multi_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>uuid_bloom_ops</literal></entry> <entry><type>uuid</type></entry> @@ -626,6 +747,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_minmax_multi_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> </tbody> </tgroup> </table> @@ -720,13 +852,13 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions using + them are shipped for in-core data types as appropriate. Additional operator + classes can be defined by the user for other data types using equivalent + definitions, without having to write any source code; appropriate catalog + entries being declared is enough. Note that assumptions about the semantics + of operator strategies are embedded in the support functions' source code. </para> <para> @@ -795,6 +927,72 @@ typedef struct BrinOpcInfo </tgroup> </table> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> To write an operator class for a complex data type which has values included within another type, it's possible to use the inclusion support diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 2e3e837faa..6184404015 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -587,6 +587,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..9b9eedbb63 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2178 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + /* (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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (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; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + FmgrInfo *cmpFn; + Datum *values; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 6a947b958b..7f2710a28d 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -116,14 +116,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -137,7 +137,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 3de5455221..6845887730 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1877,6 +1877,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1954,6 +2100,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1976,6 +2139,23 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2042,6 +2222,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2073,6 +2319,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2095,6 +2358,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2117,6 +2397,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2181,6 +2478,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2332,6 +2646,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2387,6 +2847,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2409,6 +2886,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2465,6 +2959,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2487,6 +2998,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2553,6 +3081,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 6d5b6fb455..1970fd08d6 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -943,6 +943,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1038,6 +1184,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1067,6 +1230,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1117,6 +1297,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1170,6 +1424,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1204,6 +1478,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1237,6 +1531,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1322,6 +1636,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1447,6 +1780,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1517,6 +2014,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1551,6 +2068,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1611,6 +2148,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1642,7 +2199,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1697,6 +2275,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '0', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index 092511df81..0572d529e7 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -271,18 +271,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -294,38 +303,59 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -341,36 +371,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -382,6 +430,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -391,6 +442,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -400,6 +454,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index ba428ba379..3f03ec6207 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -176,10 +176,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '5054', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '5024', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '5087', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '5027', @@ -188,10 +192,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '5059', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '5042', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '5088', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '5038', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -209,23 +217,35 @@ { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, { oid => '5049', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, +{ oid => '5051', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '5089', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, -{ oid => '5050', +{ oid => '5091', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, +{ oid => '5052', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '5064', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '5033', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '5063', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '5034', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '5066', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '5035', @@ -236,10 +256,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '5090', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '5037', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '5061', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '5041', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -248,12 +272,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '5050', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '5046', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '5065', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '5047', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fa908278d2..4c6914ad81 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8045,6 +8045,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '5068', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '5069', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '5070', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '5071', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '6015', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'bool', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '5072', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '5073', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '5074', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '5075', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '5076', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '5077', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '5078', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '5079', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '5080', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '5081', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '5082', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '5083', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '5084', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '5085', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '5086', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 739303dbbd..ac03cf1408 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 3e3e537951..fb3bb1204e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -105,6 +105,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.21.1
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-04-05 15:29                                                             ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:51                                                               ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Alexander Korotkov @ 2020-04-05 15:29 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Hi!

On Thu, Apr 2, 2020 at 5:29 AM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
> >On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
> >> Yeah, the opclass params patches got broken by 773df883e adding enum
> >> reloptions. The breakage is somewhat extensive so I'll leave it up to
> >> Nikita to fix it in [1]. Until that happens, apply the patches on
> >> top of caba97a9d9 for review.
> >
> >This has been close to two months now, so I have the patch as RwF.
> >Feel free to update if you think that's incorrect.
>
> I see the opclass parameters patch got committed a couple days ago, so
> I've rebased the patch series on top of it. The pach was marked RwF
> since 2019-11, so I'll add it to the next CF.

I think this patchset was marked RwF mainly because slow progress on
opclass parameters.  Now we got opclass parameters committed, and I
think this patchset is in a pretty good shape.  Moreover, opclass
parameters patch comes with very small examples.  This patchset would
be great showcase for opclass parameters.

I'd like to give this patchset a chance for v13.  I'm going to make
another pass trough this patchset.  If I wouldn't find serious issues,
I'm going to commit it.  Any objections?

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2020-04-05 15:51                                                               ` Tom Lane <tgl@sss.pgh.pa.us>
  2020-04-05 16:19                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  1 sibling, 1 reply; 177+ messages in thread

From: Tom Lane @ 2020-04-05 15:51 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Alexander Korotkov <a.korotkov@postgrespro.ru> writes:
> I'd like to give this patchset a chance for v13.  I'm going to make
> another pass trough this patchset.  If I wouldn't find serious issues,
> I'm going to commit it.  Any objections?

I think it is way too late to be reviving major features that nobody
has been looking at for months, that indeed were never even listed
in the final CF.  At this point in the cycle I think we should just be
trying to get small stuff over the line, not shove in major patches
and figure they can be stabilized later.

In this particular case, the last serious work on the patchset seems
to have been Tomas' revision of 2019-09-14, and he specifically stated
then that the APIs still needed work.  That doesn't sound like
"it's about ready to commit" to me.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:51                                                               ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-04-05 16:19                                                                 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 0 replies; 177+ messages in thread

From: Alexander Korotkov @ 2020-04-05 16:19 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 5, 2020 at 6:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Alexander Korotkov <a.korotkov@postgrespro.ru> writes:
> > I'd like to give this patchset a chance for v13.  I'm going to make
> > another pass trough this patchset.  If I wouldn't find serious issues,
> > I'm going to commit it.  Any objections?
>
> I think it is way too late to be reviving major features that nobody
> has been looking at for months, that indeed were never even listed
> in the final CF.  At this point in the cycle I think we should just be
> trying to get small stuff over the line, not shove in major patches
> and figure they can be stabilized later.
>
> In this particular case, the last serious work on the patchset seems
> to have been Tomas' revision of 2019-09-14, and he specifically stated
> then that the APIs still needed work.  That doesn't sound like
> "it's about ready to commit" to me.

OK, got it.  Thank you for the feedback.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2020-04-05 15:53                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  1 sibling, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-04-05 15:53 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 05, 2020 at 06:29:15PM +0300, Alexander Korotkov wrote:
>Hi!
>
>On Thu, Apr 2, 2020 at 5:29 AM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
>> >On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
>> >> Yeah, the opclass params patches got broken by 773df883e adding enum
>> >> reloptions. The breakage is somewhat extensive so I'll leave it up to
>> >> Nikita to fix it in [1]. Until that happens, apply the patches on
>> >> top of caba97a9d9 for review.
>> >
>> >This has been close to two months now, so I have the patch as RwF.
>> >Feel free to update if you think that's incorrect.
>>
>> I see the opclass parameters patch got committed a couple days ago, so
>> I've rebased the patch series on top of it. The pach was marked RwF
>> since 2019-11, so I'll add it to the next CF.
>
>I think this patchset was marked RwF mainly because slow progress on
>opclass parameters.  Now we got opclass parameters committed, and I
>think this patchset is in a pretty good shape.  Moreover, opclass
>parameters patch comes with very small examples.  This patchset would
>be great showcase for opclass parameters.
>
>I'd like to give this patchset a chance for v13.  I'm going to make
>another pass trough this patchset.  If I wouldn't find serious issues,
>I'm going to commit it.  Any objections?
>

I'm an author of the patchset and I'd love to see it committed, but I
think that might be a bit too rushed and unfair (considering it was not
included in the current CF at all).

I think the code is correct and I'm not aware of any bugs, but I'm not
sure there was sufficient discussion about things like costing, choosing
parameter values (e.g.  number of values in the multi-minmax or bloom
filter parameters).

That being said, I think the first couple of patches (that modify how
BRIN deals with multi-key scans and IS NULL clauses) are simple enough
and non-controversial, so maybe we could get 0001-0003 committed, and
leave the bloom/multi-minmax opclasses for v14.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-04-05 16:33                                                                 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alexander Korotkov @ 2020-04-05 16:33 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 5, 2020 at 6:53 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> On Sun, Apr 05, 2020 at 06:29:15PM +0300, Alexander Korotkov wrote:
> >On Thu, Apr 2, 2020 at 5:29 AM Tomas Vondra
> ><tomas.vondra@2ndquadrant.com> wrote:
> >> On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
> >> >On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
> >> >> Yeah, the opclass params patches got broken by 773df883e adding enum
> >> >> reloptions. The breakage is somewhat extensive so I'll leave it up to
> >> >> Nikita to fix it in [1]. Until that happens, apply the patches on
> >> >> top of caba97a9d9 for review.
> >> >
> >> >This has been close to two months now, so I have the patch as RwF.
> >> >Feel free to update if you think that's incorrect.
> >>
> >> I see the opclass parameters patch got committed a couple days ago, so
> >> I've rebased the patch series on top of it. The pach was marked RwF
> >> since 2019-11, so I'll add it to the next CF.
> >
> >I think this patchset was marked RwF mainly because slow progress on
> >opclass parameters.  Now we got opclass parameters committed, and I
> >think this patchset is in a pretty good shape.  Moreover, opclass
> >parameters patch comes with very small examples.  This patchset would
> >be great showcase for opclass parameters.
> >
> >I'd like to give this patchset a chance for v13.  I'm going to make
> >another pass trough this patchset.  If I wouldn't find serious issues,
> >I'm going to commit it.  Any objections?
> >
>
> I'm an author of the patchset and I'd love to see it committed, but I
> think that might be a bit too rushed and unfair (considering it was not
> included in the current CF at all).
>
> I think the code is correct and I'm not aware of any bugs, but I'm not
> sure there was sufficient discussion about things like costing, choosing
> parameter values (e.g.  number of values in the multi-minmax or bloom
> filter parameters).

Ok!

> That being said, I think the first couple of patches (that modify how
> BRIN deals with multi-key scans and IS NULL clauses) are simple enough
> and non-controversial, so maybe we could get 0001-0003 committed, and
> leave the bloom/multi-minmax opclasses for v14.

Regarding 0001-0003 I've couple of notes:
1) They should revise BRIN extensibility documentation section.
2) I think 0002 and 0003 should be merged.  NULL ScanKeys should be
still passed to consistent function when oi_regular_nulls == false.

Assuming we're not going to get 0001-0003 into v13, I'm not so
inclined to rush on these three as well.  But you're willing to commit
them, you can count round of review on me.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2020-04-05 16:59                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-04-05 16:59 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 05, 2020 at 07:33:40PM +0300, Alexander Korotkov wrote:
>On Sun, Apr 5, 2020 at 6:53 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> On Sun, Apr 05, 2020 at 06:29:15PM +0300, Alexander Korotkov wrote:
>> >On Thu, Apr 2, 2020 at 5:29 AM Tomas Vondra
>> ><tomas.vondra@2ndquadrant.com> wrote:
>> >> On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
>> >> >On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
>> >> >> Yeah, the opclass params patches got broken by 773df883e adding enum
>> >> >> reloptions. The breakage is somewhat extensive so I'll leave it up to
>> >> >> Nikita to fix it in [1]. Until that happens, apply the patches on
>> >> >> top of caba97a9d9 for review.
>> >> >
>> >> >This has been close to two months now, so I have the patch as RwF.
>> >> >Feel free to update if you think that's incorrect.
>> >>
>> >> I see the opclass parameters patch got committed a couple days ago, so
>> >> I've rebased the patch series on top of it. The pach was marked RwF
>> >> since 2019-11, so I'll add it to the next CF.
>> >
>> >I think this patchset was marked RwF mainly because slow progress on
>> >opclass parameters.  Now we got opclass parameters committed, and I
>> >think this patchset is in a pretty good shape.  Moreover, opclass
>> >parameters patch comes with very small examples.  This patchset would
>> >be great showcase for opclass parameters.
>> >
>> >I'd like to give this patchset a chance for v13.  I'm going to make
>> >another pass trough this patchset.  If I wouldn't find serious issues,
>> >I'm going to commit it.  Any objections?
>> >
>>
>> I'm an author of the patchset and I'd love to see it committed, but I
>> think that might be a bit too rushed and unfair (considering it was not
>> included in the current CF at all).
>>
>> I think the code is correct and I'm not aware of any bugs, but I'm not
>> sure there was sufficient discussion about things like costing, choosing
>> parameter values (e.g.  number of values in the multi-minmax or bloom
>> filter parameters).
>
>Ok!
>
>> That being said, I think the first couple of patches (that modify how
>> BRIN deals with multi-key scans and IS NULL clauses) are simple enough
>> and non-controversial, so maybe we could get 0001-0003 committed, and
>> leave the bloom/multi-minmax opclasses for v14.
>
>Regarding 0001-0003 I've couple of notes:
>1) They should revise BRIN extensibility documentation section.
>2) I think 0002 and 0003 should be merged.  NULL ScanKeys should be
>still passed to consistent function when oi_regular_nulls == false.
>
>Assuming we're not going to get 0001-0003 into v13, I'm not so
>inclined to rush on these three as well.  But you're willing to commit
>them, you can count round of review on me.
>

I have no intention to get 0001-0003 committed. I think those changes
are beneficial on their own, but the primary reason was to support the
new opclasses (which require those changes). And those parts are not
going to make it into v13 ...

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-04-05 17:01                                                                     ` Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-05-13 01:01                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Alexander Korotkov @ 2020-04-05 17:01 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> On Sun, Apr 05, 2020 at 07:33:40PM +0300, Alexander Korotkov wrote:
> >On Sun, Apr 5, 2020 at 6:53 PM Tomas Vondra
> ><tomas.vondra@2ndquadrant.com> wrote:
> >> On Sun, Apr 05, 2020 at 06:29:15PM +0300, Alexander Korotkov wrote:
> >> >On Thu, Apr 2, 2020 at 5:29 AM Tomas Vondra
> >> ><tomas.vondra@2ndquadrant.com> wrote:
> >> >> On Sun, Dec 01, 2019 at 10:55:02AM +0900, Michael Paquier wrote:
> >> >> >On Thu, Sep 26, 2019 at 09:01:48PM +0200, Tomas Vondra wrote:
> >> >> >> Yeah, the opclass params patches got broken by 773df883e adding enum
> >> >> >> reloptions. The breakage is somewhat extensive so I'll leave it up to
> >> >> >> Nikita to fix it in [1]. Until that happens, apply the patches on
> >> >> >> top of caba97a9d9 for review.
> >> >> >
> >> >> >This has been close to two months now, so I have the patch as RwF.
> >> >> >Feel free to update if you think that's incorrect.
> >> >>
> >> >> I see the opclass parameters patch got committed a couple days ago, so
> >> >> I've rebased the patch series on top of it. The pach was marked RwF
> >> >> since 2019-11, so I'll add it to the next CF.
> >> >
> >> >I think this patchset was marked RwF mainly because slow progress on
> >> >opclass parameters.  Now we got opclass parameters committed, and I
> >> >think this patchset is in a pretty good shape.  Moreover, opclass
> >> >parameters patch comes with very small examples.  This patchset would
> >> >be great showcase for opclass parameters.
> >> >
> >> >I'd like to give this patchset a chance for v13.  I'm going to make
> >> >another pass trough this patchset.  If I wouldn't find serious issues,
> >> >I'm going to commit it.  Any objections?
> >> >
> >>
> >> I'm an author of the patchset and I'd love to see it committed, but I
> >> think that might be a bit too rushed and unfair (considering it was not
> >> included in the current CF at all).
> >>
> >> I think the code is correct and I'm not aware of any bugs, but I'm not
> >> sure there was sufficient discussion about things like costing, choosing
> >> parameter values (e.g.  number of values in the multi-minmax or bloom
> >> filter parameters).
> >
> >Ok!
> >
> >> That being said, I think the first couple of patches (that modify how
> >> BRIN deals with multi-key scans and IS NULL clauses) are simple enough
> >> and non-controversial, so maybe we could get 0001-0003 committed, and
> >> leave the bloom/multi-minmax opclasses for v14.
> >
> >Regarding 0001-0003 I've couple of notes:
> >1) They should revise BRIN extensibility documentation section.
> >2) I think 0002 and 0003 should be merged.  NULL ScanKeys should be
> >still passed to consistent function when oi_regular_nulls == false.
> >
> >Assuming we're not going to get 0001-0003 into v13, I'm not so
> >inclined to rush on these three as well.  But you're willing to commit
> >them, you can count round of review on me.
> >
>
> I have no intention to get 0001-0003 committed. I think those changes
> are beneficial on their own, but the primary reason was to support the
> new opclasses (which require those changes). And those parts are not
> going to make it into v13 ...

OK, no problem.
Let's do this for v14.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2020-05-13 01:01                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-05-13 01:01 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Hi,

here is an updated patch series, fixing duplicate OIDs etc.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (4 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-a-20200513.patch (text/plain, 20.8K) download From 11accee6a5b20af4e09382f90912dd72e79d832a Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 1/5] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 7db3ae5ee0..f1beb2eff9 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,6 +388,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -409,6 +412,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -469,7 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -479,34 +529,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -518,12 +553,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 9edae40ed8..e5798da368 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8073,7 +8073,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8089,7 +8089,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.21.3
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200513.patch (text/plain, 9.9K) download From 6a289c4ed126c3fdf31f0cc98e54d47cb7d80e2c Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 2/5] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f1beb2eff9..25ae02a999 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,8 +388,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -414,10 +416,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,14 +444,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -454,9 +457,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,6 +560,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.21.3
0003-Move-processing-of-NULLs-from-BRIN-support--20200513.patch (text/plain, 16.1K) download From 61a3d76ebb6a3e4c36bbdea692de8ff766b66a93 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 3/5] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 25ae02a999..8c94252567 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -177,7 +180,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -241,31 +243,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -358,6 +336,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -561,69 +540,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -771,7 +712,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -800,25 +740,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1597,6 +1520,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1650,3 +1606,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.21.3
0004-BRIN-bloom-indexes-20200513.patch (text/plain, 126.7K) download From 7962a4a3e6b77596fde4df6e4ff97bda19408c52 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:57:59 +0200 Subject: [PATCH 4/5] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 215 +++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 ++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 +++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++++ 17 files changed, 2823 insertions(+), 2 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 46a7d07bf8..900cd47a4c 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -132,6 +132,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> @@ -183,6 +190,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>|&amp;&gt;</literal> </entry> </row> + <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> @@ -194,6 +208,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> @@ -205,6 +226,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> @@ -216,6 +244,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> @@ -227,6 +262,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> @@ -238,6 +280,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> @@ -261,6 +310,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&lt;&lt;</literal> </entry> </row> + <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> @@ -272,6 +328,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> @@ -283,6 +346,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -294,6 +364,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -305,6 +382,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> @@ -316,6 +400,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> @@ -327,6 +418,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> @@ -338,6 +436,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> @@ -369,6 +474,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;=</literal> </entry> </row> + <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> @@ -380,6 +492,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> @@ -391,6 +510,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> @@ -413,6 +539,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> @@ -424,6 +557,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -435,6 +575,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -446,6 +593,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -457,6 +611,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> @@ -811,6 +972,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index ff87b2d28f..f6380ef8e2 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..b119e4e264 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,980 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + Size len; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* TODO optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (values[i] != values[nvalues - 1]) + values[nvalues++] = values[i]; + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..af6891e348 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -2002,6 +2065,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2096,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2118,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2140,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2182,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2204,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2355,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2410,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2432,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2488,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2510,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2576,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..c24a80545a 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -984,6 +1125,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1178,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1212,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1245,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1297,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1330,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1455,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1525,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1559,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1619,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1652,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1705,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..16d6111ab6 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +386,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..7f733aeab1 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index e5798da368..132a274bbc 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8096,6 +8096,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 27056d70d3..729d5c04c1 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2009,6 +2009,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2072,7 +2073,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 0b990fd814..5d11d7a946 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4929,8 +4929,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 95f1925072..620b55152d 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 8ba4136220..1d59f2d90f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.21.3
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2020-07-03 00:57                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-03 00:57 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
...
>> >
>> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >inclined to rush on these three as well.  But you're willing to commit
>> >them, you can count round of review on me.
>> >
>>
>> I have no intention to get 0001-0003 committed. I think those changes
>> are beneficial on their own, but the primary reason was to support the
>> new opclasses (which require those changes). And those parts are not
>> going to make it into v13 ...
>
>OK, no problem.
>Let's do this for v14.
>

Hi Alexander,

Are you still interested in reviewing those patches? I'll take a look at
0001-0003 to check that your previous feedback was addressed. Do you
have any comments about 0004 / 0005, which I think are the more
interesting parts of this series?


Attached is a rebased version - I realized I forgot to include 0005 in
the last update, for some reason.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 

Attachments: download all (5 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-a-20200703.patch (text/plain, 20.8K) download From ac8e74149e8ce6c291e8c414872e1fb2903836d0 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 1/5] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 7db3ae5ee0..f1beb2eff9 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,6 +388,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -409,6 +412,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -469,7 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -479,34 +529,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -518,12 +553,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 38295aca48..46cedaeee6 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8076,7 +8076,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8092,7 +8092,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200703.patch (text/plain, 9.9K) download From 1f175a68c2d5332bb86d62036bc8771b233b8791 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 2/5] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f1beb2eff9..25ae02a999 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -388,8 +388,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -414,10 +416,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,14 +444,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -454,9 +457,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,6 +560,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.25.4
0003-Move-processing-of-NULLs-from-BRIN-support--20200703.patch (text/plain, 16.1K) download From f77e38ab6997775a75d0ca03fd8c2ced094dceeb Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 3/5] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 25ae02a999..8c94252567 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -177,7 +180,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -241,31 +243,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -358,6 +336,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -561,69 +540,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -771,7 +712,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -800,25 +740,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1597,6 +1520,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1650,3 +1606,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0004-BRIN-bloom-indexes-20200703.patch (text/plain, 126.7K) download From 9d01bf0157c545bbad5699b38afcaf6adbb820a5 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:57:59 +0200 Subject: [PATCH 4/5] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 215 +++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 ++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 +++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++++ 17 files changed, 2823 insertions(+), 2 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4c5eeb875f..cc3533ef1f 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -132,6 +132,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> @@ -183,6 +190,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>|&amp;&gt;</literal> </entry> </row> + <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> @@ -194,6 +208,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> @@ -205,6 +226,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> @@ -216,6 +244,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> @@ -227,6 +262,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> @@ -238,6 +280,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> @@ -261,6 +310,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&lt;&lt;</literal> </entry> </row> + <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> @@ -272,6 +328,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> @@ -283,6 +346,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -294,6 +364,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -305,6 +382,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> @@ -316,6 +400,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> @@ -327,6 +418,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> @@ -338,6 +436,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> @@ -369,6 +474,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;=</literal> </entry> </row> + <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> @@ -380,6 +492,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> @@ -391,6 +510,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> @@ -413,6 +539,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> @@ -424,6 +557,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -435,6 +575,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -446,6 +593,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -457,6 +611,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> @@ -841,6 +1002,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..b119e4e264 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,980 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + Size len; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* TODO optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (values[i] != values[nvalues - 1]) + values[nvalues++] = values[i]; + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..af6891e348 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -2002,6 +2065,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2096,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2118,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2140,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2182,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2204,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2355,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2410,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2432,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2488,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2510,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2576,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..c24a80545a 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -984,6 +1125,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1178,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1212,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1245,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1297,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1330,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1455,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1525,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1559,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1619,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1652,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1705,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..16d6111ab6 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +386,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..7f733aeab1 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 46cedaeee6..5e0d29b12d 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8099,6 +8099,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 27056d70d3..729d5c04c1 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2009,6 +2009,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2072,7 +2073,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 7d2d6328fc..97f0c47005 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4929,8 +4929,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..74db99976a 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-BRIN-multi-range-minmax-indexes-20200703.patch (text/plain, 198.0K) download From ba0c3abbbc456ebcc4187c7a8fdfd0fc6e0236c5 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 3 Jul 2020 02:14:59 +0200 Subject: [PATCH 5/5] BRIN multi-range minmax indexes --- doc/src/sgml/brin.sgml | 212 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2178 +++++++++++++++++++ src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 545 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 65 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 ++++ 17 files changed, 4498 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index cc3533ef1f..1bf2e0dce7 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -262,6 +262,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_minmax_multi_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>float8_bloom_ops</literal></entry> <entry><type>double precision</type></entry> @@ -280,6 +291,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_minmax_multi_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>inet_bloom_ops</literal></entry> <entry><type>inet</type></entry> @@ -298,6 +320,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_minmax_multi_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>network_inclusion_ops</literal></entry> <entry><type>inet</type></entry> @@ -346,6 +379,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_minmax_multi_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr_bloom_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -364,6 +408,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr8_bloom_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -382,6 +437,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>name_bloom_ops</literal></entry> <entry><type>name</type></entry> @@ -436,6 +502,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>oid_bloom_ops</literal></entry> <entry><type>oid</type></entry> @@ -557,6 +634,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timestamptz_bloom_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -575,6 +663,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>time_bloom_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -593,6 +692,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_minmax_multi_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timetz_bloom_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -611,6 +721,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_minmax_multi_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>uuid_bloom_ops</literal></entry> <entry><type>uuid</type></entry> @@ -629,6 +750,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_minmax_multi_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> </tbody> </tgroup> </table> @@ -753,13 +885,13 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions using + them are shipped for in-core data types as appropriate. Additional operator + classes can be defined by the user for other data types using equivalent + definitions, without having to write any source code; appropriate catalog + entries being declared is enough. Note that assumptions about the semantics + of operator strategies are embedded in the support functions' source code. </para> <para> @@ -830,6 +962,72 @@ typedef struct BrinOpcInfo </tgroup> </table> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> To write an operator class for a complex data type which has values included within another type, it's possible to use the inclusion support diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..f6b8fbd1d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..9b9eedbb63 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2178 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + /* (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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (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; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + FmgrInfo *cmpFn; + Datum *values; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/transam.h b/src/include/access/transam.h index a91a0c7487..18a8fd77d9 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -130,14 +130,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -151,7 +151,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index af6891e348..31ec56766a 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1999,6 +2162,23 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2065,6 +2245,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2096,6 +2342,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2118,6 +2381,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2140,6 +2420,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2204,6 +2501,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2355,6 +2669,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2410,6 +2870,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2432,6 +2909,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2488,6 +2982,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2510,6 +3021,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2576,6 +3104,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index c24a80545a..317c9475f8 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1075,6 +1238,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1125,6 +1305,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1178,6 +1432,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1212,6 +1486,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1245,6 +1539,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1330,6 +1644,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1455,6 +1788,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1525,6 +2022,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1559,6 +2076,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1619,6 +2156,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1650,7 +2207,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1705,6 +2283,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index 16d6111ab6..92b3a577ea 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,38 +307,59 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -345,36 +375,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -386,6 +434,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -395,6 +446,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -404,6 +458,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 7f733aeab1..8bbf753f65 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,24 +220,36 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -240,10 +260,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -252,12 +276,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 5e0d29b12d..fa324f4608 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8082,6 +8082,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 97f0c47005..f18097a02b 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4926,12 +4926,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 74db99976a..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-10 09:01                                                                         ` Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Masahiko Sawada @ 2020-07-10 09:01 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>
> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
> ><tomas.vondra@2ndquadrant.com> wrote:
> ...
> >> >
> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
> >> >inclined to rush on these three as well.  But you're willing to commit
> >> >them, you can count round of review on me.
> >> >
> >>
> >> I have no intention to get 0001-0003 committed. I think those changes
> >> are beneficial on their own, but the primary reason was to support the
> >> new opclasses (which require those changes). And those parts are not
> >> going to make it into v13 ...
> >
> >OK, no problem.
> >Let's do this for v14.
> >
>
> Hi Alexander,
>
> Are you still interested in reviewing those patches? I'll take a look at
> 0001-0003 to check that your previous feedback was addressed. Do you
> have any comments about 0004 / 0005, which I think are the more
> interesting parts of this series?
>
>
> Attached is a rebased version - I realized I forgot to include 0005 in
> the last update, for some reason.
>

I've done a quick test with this patch set. I wonder if we can improve
brin_page_items() SQL function in pageinspect as well. Currently,
brin_page_items() is hard-coded to support only normal brin indexes.
When we pass brin-bloom or brin-multi-range to that function the
binary values are shown in 'value' column but it seems not helpful for
users. For instance, here is an output of brin_page_items() with a
brin-multi-range index:

postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
2), 'mul');
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------
----------------------------
itemoffset  | 1
blknum      | 0
attnum      | 1
allnulls    | f
hasnulls    | f
placeholder | f
value       | {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
00000710000}

Also, I got an assertion failure when setting false_positive_rate reloption:

postgres(1:12448)=# create index blm on t using brin (c int4_bloom_ops
(false_positive_rate = 1));
TRAP: FailedAssertion("(false_positive_rate > 0) &&
(false_positive_rate < 1.0)", File: "brin_bloom.c", Line: 300)

I'll look at the code in depth and let you know if I find a problem.

Regards,

--
Masahiko Sawada            http://www.2ndQuadrant.com/

PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
@ 2020-07-10 12:09                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-07-10 12:09 UTC (permalink / raw)
  To: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
>On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <tomas.vondra@2ndquadrant.com> wrote:
>>
>> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
>> ><tomas.vondra@2ndquadrant.com> wrote:
>> ...
>> >> >
>> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >> >inclined to rush on these three as well.  But you're willing to commit
>> >> >them, you can count round of review on me.
>> >> >
>> >>
>> >> I have no intention to get 0001-0003 committed. I think those changes
>> >> are beneficial on their own, but the primary reason was to support the
>> >> new opclasses (which require those changes). And those parts are not
>> >> going to make it into v13 ...
>> >
>> >OK, no problem.
>> >Let's do this for v14.
>> >
>>
>> Hi Alexander,
>>
>> Are you still interested in reviewing those patches? I'll take a look at
>> 0001-0003 to check that your previous feedback was addressed. Do you
>> have any comments about 0004 / 0005, which I think are the more
>> interesting parts of this series?
>>
>>
>> Attached is a rebased version - I realized I forgot to include 0005 in
>> the last update, for some reason.
>>
>
>I've done a quick test with this patch set. I wonder if we can improve
>brin_page_items() SQL function in pageinspect as well. Currently,
>brin_page_items() is hard-coded to support only normal brin indexes.
>When we pass brin-bloom or brin-multi-range to that function the
>binary values are shown in 'value' column but it seems not helpful for
>users. For instance, here is an output of brin_page_items() with a
>brin-multi-range index:
>
>postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
>2), 'mul');
>-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------
>-----------------------------------------------------------------------------------------------------------------------------------
>----------------------------
>itemoffset  | 1
>blknum      | 0
>attnum      | 1
>allnulls    | f
>hasnulls    | f
>placeholder | f
>value       | {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
>00000710000}
>

Hmm. I'm not sure we can do much better, without making the function
much more complicated. I mean, even with regular BRIN indexes we don't
really know if the value is plain min/max, right?


>Also, I got an assertion failure when setting false_positive_rate reloption:
>
>postgres(1:12448)=# create index blm on t using brin (c int4_bloom_ops
>(false_positive_rate = 1));
>TRAP: FailedAssertion("(false_positive_rate > 0) &&
>(false_positive_rate < 1.0)", File: "brin_bloom.c", Line: 300)
>
>I'll look at the code in depth and let you know if I find a problem.
>

Yeah, the assert should say (f_p_r <= 1.0).

But I'm not convinced we should allow values up to 1.0, really. The
f_p_r is the fraction of the table that will get matched always, so 1.0
would mean we get to scan the whole table. Seems kinda pointless. So
maybe we should cap it to something like 0.1 or so, but I agree the
value seems kinda arbitrary.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-10 14:44                                                                             ` Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 11:24                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Sascha Kuhl @ 2020-07-10 14:44 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli 2020,
14:09:

> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <tomas.vondra@2ndquadrant.com>
> wrote:
> >>
> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
> >> ><tomas.vondra@2ndquadrant.com> wrote:
> >> ...
> >> >> >
> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
> >> >> >inclined to rush on these three as well.  But you're willing to
> commit
> >> >> >them, you can count round of review on me.
> >> >> >
> >> >>
> >> >> I have no intention to get 0001-0003 committed. I think those changes
> >> >> are beneficial on their own, but the primary reason was to support
> the
> >> >> new opclasses (which require those changes). And those parts are not
> >> >> going to make it into v13 ...
> >> >
> >> >OK, no problem.
> >> >Let's do this for v14.
> >> >
> >>
> >> Hi Alexander,
> >>
> >> Are you still interested in reviewing those patches? I'll take a look at
> >> 0001-0003 to check that your previous feedback was addressed. Do you
> >> have any comments about 0004 / 0005, which I think are the more
> >> interesting parts of this series?
> >>
> >>
> >> Attached is a rebased version - I realized I forgot to include 0005 in
> >> the last update, for some reason.
> >>
> >
> >I've done a quick test with this patch set. I wonder if we can improve
> >brin_page_items() SQL function in pageinspect as well. Currently,
> >brin_page_items() is hard-coded to support only normal brin indexes.
> >When we pass brin-bloom or brin-multi-range to that function the
> >binary values are shown in 'value' column but it seems not helpful for
> >users. For instance, here is an output of brin_page_items() with a
> >brin-multi-range index:
> >
> >postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
> >2), 'mul');
> >-[ RECORD 1
> ]----------------------------------------------------------------------------------------------------------------------
>
> >-----------------------------------------------------------------------------------------------------------------------------------
> >----------------------------
> >itemoffset  | 1
> >blknum      | 0
> >attnum      | 1
> >allnulls    | f
> >hasnulls    | f
> >placeholder | f
> >value       |
> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>
> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
> >00000710000}
> >
>
> Hmm. I'm not sure we can do much better, without making the function
> much more complicated. I mean, even with regular BRIN indexes we don't
> really know if the value is plain min/max, right?
>
You can be sure with the next node. The value is in can be false positiv.
The value is out is clear. You can detect the change between in and out.

>
>
> >Also, I got an assertion failure when setting false_positive_rate
> reloption:
> >
> >postgres(1:12448)=# create index blm on t using brin (c int4_bloom_ops
> >(false_positive_rate = 1));
> >TRAP: FailedAssertion("(false_positive_rate > 0) &&
> >(false_positive_rate < 1.0)", File: "brin_bloom.c", Line: 300)
> >
> >I'll look at the code in depth and let you know if I find a problem.
> >
>
> Yeah, the assert should say (f_p_r <= 1.0).
>
> But I'm not convinced we should allow values up to 1.0, really. The
> f_p_r is the fraction of the table that will get matched always, so 1.0
> would mean we get to scan the whole table. Seems kinda pointless. So
> maybe we should cap it to something like 0.1 or so, but I agree the
> value seems kinda arbitrary.
>
>
> regards
>
> --
> Tomas Vondra                  http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
>
>

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
@ 2020-07-11 11:24                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-11 13:32                                                                                 ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-11 11:24 UTC (permalink / raw)
  To: Sascha Kuhl <yogidabanli@gmail.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Fri, Jul 10, 2020 at 04:44:41PM +0200, Sascha Kuhl wrote:
>Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli 2020,
>14:09:
>
>> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
>> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <tomas.vondra@2ndquadrant.com>
>> wrote:
>> >>
>> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
>> >> ><tomas.vondra@2ndquadrant.com> wrote:
>> >> ...
>> >> >> >
>> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >> >> >inclined to rush on these three as well.  But you're willing to
>> commit
>> >> >> >them, you can count round of review on me.
>> >> >> >
>> >> >>
>> >> >> I have no intention to get 0001-0003 committed. I think those changes
>> >> >> are beneficial on their own, but the primary reason was to support
>> the
>> >> >> new opclasses (which require those changes). And those parts are not
>> >> >> going to make it into v13 ...
>> >> >
>> >> >OK, no problem.
>> >> >Let's do this for v14.
>> >> >
>> >>
>> >> Hi Alexander,
>> >>
>> >> Are you still interested in reviewing those patches? I'll take a look at
>> >> 0001-0003 to check that your previous feedback was addressed. Do you
>> >> have any comments about 0004 / 0005, which I think are the more
>> >> interesting parts of this series?
>> >>
>> >>
>> >> Attached is a rebased version - I realized I forgot to include 0005 in
>> >> the last update, for some reason.
>> >>
>> >
>> >I've done a quick test with this patch set. I wonder if we can improve
>> >brin_page_items() SQL function in pageinspect as well. Currently,
>> >brin_page_items() is hard-coded to support only normal brin indexes.
>> >When we pass brin-bloom or brin-multi-range to that function the
>> >binary values are shown in 'value' column but it seems not helpful for
>> >users. For instance, here is an output of brin_page_items() with a
>> >brin-multi-range index:
>> >
>> >postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
>> >2), 'mul');
>> >-[ RECORD 1
>> ]----------------------------------------------------------------------------------------------------------------------
>>
>> >-----------------------------------------------------------------------------------------------------------------------------------
>> >----------------------------
>> >itemoffset  | 1
>> >blknum      | 0
>> >attnum      | 1
>> >allnulls    | f
>> >hasnulls    | f
>> >placeholder | f
>> >value       |
>> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>>
>> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
>> >00000710000}
>> >
>>
>> Hmm. I'm not sure we can do much better, without making the function
>> much more complicated. I mean, even with regular BRIN indexes we don't
>> really know if the value is plain min/max, right?
>>
>You can be sure with the next node. The value is in can be false positiv.
>The value is out is clear. You can detect the change between in and out.
>

I'm sorry, I don't understand what you're suggesting. How is any of this
related to false positive rate, etc?

The problem here is that while plain BRIN opclasses have fairly simple
summary that can be stored using a fixed number of simple data types
(e.g. minmax will store two values with the same data types as the
indexd column)

     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
                      sizeof(MinmaxOpaque));
     result->oi_nstored = 2;
     result->oi_opaque = (MinmaxOpaque *)
         MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
     result->oi_typcache[0] = result->oi_typcache[1] =
         lookup_type_cache(typoid, 0);

The opclassed introduced here have somewhat more complex summary, stored
as a single bytea value - which is what gets printed by brin_page_items.

To print something easier to read (for humans) we'd either have to teach
brin_page_items about the diffrent opclasses (multi-range, bloom) end
how to parse the summary bytea, or we'd have to extend the opclasses
with a function formatting the summary. Or rework how the summary is
stored, but that seems like the worst option.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 11:24                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-11 13:32                                                                                 ` Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 13:37                                                                                   ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 23:30                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Sascha Kuhl @ 2020-07-11 13:32 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Sa., 11. Juli 2020,
13:24:

> On Fri, Jul 10, 2020 at 04:44:41PM +0200, Sascha Kuhl wrote:
> >Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli
> 2020,
> >14:09:
> >
> >> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
> >> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <
> tomas.vondra@2ndquadrant.com>
> >> wrote:
> >> >>
> >> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
> >> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
> >> >> ><tomas.vondra@2ndquadrant.com> wrote:
> >> >> ...
> >> >> >> >
> >> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
> >> >> >> >inclined to rush on these three as well.  But you're willing to
> >> commit
> >> >> >> >them, you can count round of review on me.
> >> >> >> >
> >> >> >>
> >> >> >> I have no intention to get 0001-0003 committed. I think those
> changes
> >> >> >> are beneficial on their own, but the primary reason was to support
> >> the
> >> >> >> new opclasses (which require those changes). And those parts are
> not
> >> >> >> going to make it into v13 ...
> >> >> >
> >> >> >OK, no problem.
> >> >> >Let's do this for v14.
> >> >> >
> >> >>
> >> >> Hi Alexander,
> >> >>
> >> >> Are you still interested in reviewing those patches? I'll take a
> look at
> >> >> 0001-0003 to check that your previous feedback was addressed. Do you
> >> >> have any comments about 0004 / 0005, which I think are the more
> >> >> interesting parts of this series?
> >> >>
> >> >>
> >> >> Attached is a rebased version - I realized I forgot to include 0005
> in
> >> >> the last update, for some reason.
> >> >>
> >> >
> >> >I've done a quick test with this patch set. I wonder if we can improve
> >> >brin_page_items() SQL function in pageinspect as well. Currently,
> >> >brin_page_items() is hard-coded to support only normal brin indexes.
> >> >When we pass brin-bloom or brin-multi-range to that function the
> >> >binary values are shown in 'value' column but it seems not helpful for
> >> >users. For instance, here is an output of brin_page_items() with a
> >> >brin-multi-range index:
> >> >
> >> >postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
> >> >2), 'mul');
> >> >-[ RECORD 1
> >>
> ]----------------------------------------------------------------------------------------------------------------------
> >>
> >>
> >-----------------------------------------------------------------------------------------------------------------------------------
> >> >----------------------------
> >> >itemoffset  | 1
> >> >blknum      | 0
> >> >attnum      | 1
> >> >allnulls    | f
> >> >hasnulls    | f
> >> >placeholder | f
> >> >value       |
> >>
> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
> >>
> >>
> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
> >> >00000710000}
> >> >
> >>
> >> Hmm. I'm not sure we can do much better, without making the function
> >> much more complicated. I mean, even with regular BRIN indexes we don't
> >> really know if the value is plain min/max, right?
> >>
> >You can be sure with the next node. The value is in can be false positiv.
> >The value is out is clear. You can detect the change between in and out.
> >
>
> I'm sorry, I don't understand what you're suggesting. How is any of this
> related to false positive rate, etc?
>

Hi,

You check by the bloom filter if a value you're searching is part of the
node, right?

In case, the value is in the bloom filter you could be mistaken, because
another value could have the same hash profile, no?

However if the value is out, the filter can not react. You can be sure that
the value is out.

If you looking for a range or many ranges of values, you traverse many
nodes. By knowing the value is out, you can state a clear set of nodes that
form the range. However the border is somehow unsharp because of the false
positives.

I am not sure if we write about the same. Please confirm, this can be
needed. Please.

I will try to understand what you write. Interesting

Sascha

>
> The problem here is that while plain BRIN opclasses have fairly simple
> summary that can be stored using a fixed number of simple data types
> (e.g. minmax will store two values with the same data types as the
> indexd column)
>
>      result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
>                       sizeof(MinmaxOpaque));
>      result->oi_nstored = 2;
>      result->oi_opaque = (MinmaxOpaque *)
>          MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
>      result->oi_typcache[0] = result->oi_typcache[1] =
>          lookup_type_cache(typoid, 0);
>
> The opclassed introduced here have somewhat more complex summary, stored
> as a single bytea value - which is what gets printed by brin_page_items.
>
> To print something easier to read (for humans) we'd either have to teach
> brin_page_items about the diffrent opclasses (multi-range, bloom) end
> how to parse the summary bytea, or we'd have to extend the opclasses
> with a function formatting the summary. Or rework how the summary is
> stored, but that seems like the worst option.
>
>
> regards
>
> --
> Tomas Vondra                  http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 11:24                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-11 13:32                                                                                 ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
@ 2020-07-11 13:37                                                                                   ` Sascha Kuhl <yogidabanli@gmail.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Sascha Kuhl @ 2020-07-11 13:37 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Sorry, my topic is different

Sascha Kuhl <yogidabanli@gmail.com> schrieb am Sa., 11. Juli 2020, 15:32:

>
>
>
> Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Sa., 11. Juli
> 2020, 13:24:
>
>> On Fri, Jul 10, 2020 at 04:44:41PM +0200, Sascha Kuhl wrote:
>> >Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli
>> 2020,
>> >14:09:
>> >
>> >> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
>> >> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <
>> tomas.vondra@2ndquadrant.com>
>> >> wrote:
>> >> >>
>> >> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>> >> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
>> >> >> ><tomas.vondra@2ndquadrant.com> wrote:
>> >> >> ...
>> >> >> >> >
>> >> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >> >> >> >inclined to rush on these three as well.  But you're willing to
>> >> commit
>> >> >> >> >them, you can count round of review on me.
>> >> >> >> >
>> >> >> >>
>> >> >> >> I have no intention to get 0001-0003 committed. I think those
>> changes
>> >> >> >> are beneficial on their own, but the primary reason was to
>> support
>> >> the
>> >> >> >> new opclasses (which require those changes). And those parts are
>> not
>> >> >> >> going to make it into v13 ...
>> >> >> >
>> >> >> >OK, no problem.
>> >> >> >Let's do this for v14.
>> >> >> >
>> >> >>
>> >> >> Hi Alexander,
>> >> >>
>> >> >> Are you still interested in reviewing those patches? I'll take a
>> look at
>> >> >> 0001-0003 to check that your previous feedback was addressed. Do you
>> >> >> have any comments about 0004 / 0005, which I think are the more
>> >> >> interesting parts of this series?
>> >> >>
>> >> >>
>> >> >> Attached is a rebased version - I realized I forgot to include 0005
>> in
>> >> >> the last update, for some reason.
>> >> >>
>> >> >
>> >> >I've done a quick test with this patch set. I wonder if we can improve
>> >> >brin_page_items() SQL function in pageinspect as well. Currently,
>> >> >brin_page_items() is hard-coded to support only normal brin indexes.
>> >> >When we pass brin-bloom or brin-multi-range to that function the
>> >> >binary values are shown in 'value' column but it seems not helpful for
>> >> >users. For instance, here is an output of brin_page_items() with a
>> >> >brin-multi-range index:
>> >> >
>> >> >postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
>> >> >2), 'mul');
>> >> >-[ RECORD 1
>> >>
>> ]----------------------------------------------------------------------------------------------------------------------
>> >>
>> >>
>> >-----------------------------------------------------------------------------------------------------------------------------------
>> >> >----------------------------
>> >> >itemoffset  | 1
>> >> >blknum      | 0
>> >> >attnum      | 1
>> >> >allnulls    | f
>> >> >hasnulls    | f
>> >> >placeholder | f
>> >> >value       |
>> >>
>> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>> >>
>> >>
>> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
>> >> >00000710000}
>> >> >
>> >>
>> >> Hmm. I'm not sure we can do much better, without making the function
>> >> much more complicated. I mean, even with regular BRIN indexes we don't
>> >> really know if the value is plain min/max, right?
>> >>
>> >You can be sure with the next node. The value is in can be false positiv.
>> >The value is out is clear. You can detect the change between in and out.
>> >
>>
>> I'm sorry, I don't understand what you're suggesting. How is any of this
>> related to false positive rate, etc?
>>
>
> Hi,
>
> You check by the bloom filter if a value you're searching is part of the
> node, right?
>
> In case, the value is in the bloom filter you could be mistaken, because
> another value could have the same hash profile, no?
>
> However if the value is out, the filter can not react. You can be sure
> that the value is out.
>
> If you looking for a range or many ranges of values, you traverse many
> nodes. By knowing the value is out, you can state a clear set of nodes that
> form the range. However the border is somehow unsharp because of the false
> positives.
>
> I am not sure if we write about the same. Please confirm, this can be
> needed. Please.
>
> I will try to understand what you write. Interesting
>
> Sascha
>
>>
>> The problem here is that while plain BRIN opclasses have fairly simple
>> summary that can be stored using a fixed number of simple data types
>> (e.g. minmax will store two values with the same data types as the
>> indexd column)
>>
>>      result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
>>                       sizeof(MinmaxOpaque));
>>      result->oi_nstored = 2;
>>      result->oi_opaque = (MinmaxOpaque *)
>>          MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
>>      result->oi_typcache[0] = result->oi_typcache[1] =
>>          lookup_type_cache(typoid, 0);
>>
>> The opclassed introduced here have somewhat more complex summary, stored
>> as a single bytea value - which is what gets printed by brin_page_items.
>>
>> To print something easier to read (for humans) we'd either have to teach
>> brin_page_items about the diffrent opclasses (multi-range, bloom) end
>> how to parse the summary bytea, or we'd have to extend the opclasses
>> with a function formatting the summary. Or rework how the summary is
>> stored, but that seems like the worst option.
>>
>>
>> regards
>>
>> --
>> Tomas Vondra                  http://www.2ndQuadrant.com
>> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>>
>

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 11:24                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-11 13:32                                                                                 ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
@ 2020-07-11 23:30                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 00:06                                                                                     ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-11 23:30 UTC (permalink / raw)
  To: Sascha Kuhl <yogidabanli@gmail.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sat, Jul 11, 2020 at 03:32:43PM +0200, Sascha Kuhl wrote:
>Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Sa., 11. Juli 2020,
>13:24:
>
>> On Fri, Jul 10, 2020 at 04:44:41PM +0200, Sascha Kuhl wrote:
>> >Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli
>> 2020,
>> >14:09:
>> >
>> >> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
>> >> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <
>> tomas.vondra@2ndquadrant.com>
>> >> wrote:
>> >> >>
>> >> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>> >> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
>> >> >> ><tomas.vondra@2ndquadrant.com> wrote:
>> >> >> ...
>> >> >> >> >
>> >> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >> >> >> >inclined to rush on these three as well.  But you're willing to
>> >> commit
>> >> >> >> >them, you can count round of review on me.
>> >> >> >> >
>> >> >> >>
>> >> >> >> I have no intention to get 0001-0003 committed. I think those
>> changes
>> >> >> >> are beneficial on their own, but the primary reason was to support
>> >> the
>> >> >> >> new opclasses (which require those changes). And those parts are
>> not
>> >> >> >> going to make it into v13 ...
>> >> >> >
>> >> >> >OK, no problem.
>> >> >> >Let's do this for v14.
>> >> >> >
>> >> >>
>> >> >> Hi Alexander,
>> >> >>
>> >> >> Are you still interested in reviewing those patches? I'll take a
>> look at
>> >> >> 0001-0003 to check that your previous feedback was addressed. Do you
>> >> >> have any comments about 0004 / 0005, which I think are the more
>> >> >> interesting parts of this series?
>> >> >>
>> >> >>
>> >> >> Attached is a rebased version - I realized I forgot to include 0005
>> in
>> >> >> the last update, for some reason.
>> >> >>
>> >> >
>> >> >I've done a quick test with this patch set. I wonder if we can improve
>> >> >brin_page_items() SQL function in pageinspect as well. Currently,
>> >> >brin_page_items() is hard-coded to support only normal brin indexes.
>> >> >When we pass brin-bloom or brin-multi-range to that function the
>> >> >binary values are shown in 'value' column but it seems not helpful for
>> >> >users. For instance, here is an output of brin_page_items() with a
>> >> >brin-multi-range index:
>> >> >
>> >> >postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
>> >> >2), 'mul');
>> >> >-[ RECORD 1
>> >>
>> ]----------------------------------------------------------------------------------------------------------------------
>> >>
>> >>
>> >-----------------------------------------------------------------------------------------------------------------------------------
>> >> >----------------------------
>> >> >itemoffset  | 1
>> >> >blknum      | 0
>> >> >attnum      | 1
>> >> >allnulls    | f
>> >> >hasnulls    | f
>> >> >placeholder | f
>> >> >value       |
>> >>
>> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>> >>
>> >>
>> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
>> >> >00000710000}
>> >> >
>> >>
>> >> Hmm. I'm not sure we can do much better, without making the function
>> >> much more complicated. I mean, even with regular BRIN indexes we don't
>> >> really know if the value is plain min/max, right?
>> >>
>> >You can be sure with the next node. The value is in can be false positiv.
>> >The value is out is clear. You can detect the change between in and out.
>> >
>>
>> I'm sorry, I don't understand what you're suggesting. How is any of this
>> related to false positive rate, etc?
>>
>
>Hi,
>
>You check by the bloom filter if a value you're searching is part of the
>node, right?
>
>In case, the value is in the bloom filter you could be mistaken, because
>another value could have the same hash profile, no?
>
>However if the value is out, the filter can not react. You can be sure that
>the value is out.
>
>If you looking for a range or many ranges of values, you traverse many
>nodes. By knowing the value is out, you can state a clear set of nodes that
>form the range. However the border is somehow unsharp because of the false
>positives.
>
>I am not sure if we write about the same. Please confirm, this can be
>needed. Please.
>

Probably not. Masahiko-san pointed out that pageinspect (which also has
a function to print pages from a BRIN index) does not understand the
summary of the new opclasses and just prints the bytea verbatim.

That has nothing to do with inspecting the bloom filter, or anything
like that. So I think there's some confusion ...


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 14:44                                                                             ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 11:24                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-11 13:32                                                                                 ` Re: WIP: BRIN multi-range indexes Sascha Kuhl <yogidabanli@gmail.com>
  2020-07-11 23:30                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-12 00:06                                                                                     ` Sascha Kuhl <yogidabanli@gmail.com>
  0 siblings, 0 replies; 177+ messages in thread

From: Sascha Kuhl @ 2020-07-12 00:06 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Alvaro Herrera <alvherre@2ndquadrant.com>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Thanks, I see there is some understanding, though.

Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am So., 12. Juli 2020,
01:30:

> On Sat, Jul 11, 2020 at 03:32:43PM +0200, Sascha Kuhl wrote:
> >Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Sa., 11. Juli
> 2020,
> >13:24:
> >
> >> On Fri, Jul 10, 2020 at 04:44:41PM +0200, Sascha Kuhl wrote:
> >> >Tomas Vondra <tomas.vondra@2ndquadrant.com> schrieb am Fr., 10. Juli
> >> 2020,
> >> >14:09:
> >> >
> >> >> On Fri, Jul 10, 2020 at 06:01:58PM +0900, Masahiko Sawada wrote:
> >> >> >On Fri, 3 Jul 2020 at 09:58, Tomas Vondra <
> >> tomas.vondra@2ndquadrant.com>
> >> >> wrote:
> >> >> >>
> >> >> >> On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov
> wrote:
> >> >> >> >On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
> >> >> >> ><tomas.vondra@2ndquadrant.com> wrote:
> >> >> >> ...
> >> >> >> >> >
> >> >> >> >> >Assuming we're not going to get 0001-0003 into v13, I'm not so
> >> >> >> >> >inclined to rush on these three as well.  But you're willing
> to
> >> >> commit
> >> >> >> >> >them, you can count round of review on me.
> >> >> >> >> >
> >> >> >> >>
> >> >> >> >> I have no intention to get 0001-0003 committed. I think those
> >> changes
> >> >> >> >> are beneficial on their own, but the primary reason was to
> support
> >> >> the
> >> >> >> >> new opclasses (which require those changes). And those parts
> are
> >> not
> >> >> >> >> going to make it into v13 ...
> >> >> >> >
> >> >> >> >OK, no problem.
> >> >> >> >Let's do this for v14.
> >> >> >> >
> >> >> >>
> >> >> >> Hi Alexander,
> >> >> >>
> >> >> >> Are you still interested in reviewing those patches? I'll take a
> >> look at
> >> >> >> 0001-0003 to check that your previous feedback was addressed. Do
> you
> >> >> >> have any comments about 0004 / 0005, which I think are the more
> >> >> >> interesting parts of this series?
> >> >> >>
> >> >> >>
> >> >> >> Attached is a rebased version - I realized I forgot to include
> 0005
> >> in
> >> >> >> the last update, for some reason.
> >> >> >>
> >> >> >
> >> >> >I've done a quick test with this patch set. I wonder if we can
> improve
> >> >> >brin_page_items() SQL function in pageinspect as well. Currently,
> >> >> >brin_page_items() is hard-coded to support only normal brin indexes.
> >> >> >When we pass brin-bloom or brin-multi-range to that function the
> >> >> >binary values are shown in 'value' column but it seems not helpful
> for
> >> >> >users. For instance, here is an output of brin_page_items() with a
> >> >> >brin-multi-range index:
> >> >> >
> >> >> >postgres(1:12801)=# select * from
> brin_page_items(get_raw_page('mul',
> >> >> >2), 'mul');
> >> >> >-[ RECORD 1
> >> >>
> >>
> ]----------------------------------------------------------------------------------------------------------------------
> >> >>
> >> >>
> >>
> >-----------------------------------------------------------------------------------------------------------------------------------
> >> >> >----------------------------
> >> >> >itemoffset  | 1
> >> >> >blknum      | 0
> >> >> >attnum      | 1
> >> >> >allnulls    | f
> >> >> >hasnulls    | f
> >> >> >placeholder | f
> >> >> >value       |
> >> >>
> >>
> {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
> >> >>
> >> >>
> >>
> >700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
> >> >> >00000710000}
> >> >> >
> >> >>
> >> >> Hmm. I'm not sure we can do much better, without making the function
> >> >> much more complicated. I mean, even with regular BRIN indexes we
> don't
> >> >> really know if the value is plain min/max, right?
> >> >>
> >> >You can be sure with the next node. The value is in can be false
> positiv.
> >> >The value is out is clear. You can detect the change between in and
> out.
> >> >
> >>
> >> I'm sorry, I don't understand what you're suggesting. How is any of this
> >> related to false positive rate, etc?
> >>
> >
> >Hi,
> >
> >You check by the bloom filter if a value you're searching is part of the
> >node, right?
> >
> >In case, the value is in the bloom filter you could be mistaken, because
> >another value could have the same hash profile, no?
> >
> >However if the value is out, the filter can not react. You can be sure
> that
> >the value is out.
> >
> >If you looking for a range or many ranges of values, you traverse many
> >nodes. By knowing the value is out, you can state a clear set of nodes
> that
> >form the range. However the border is somehow unsharp because of the false
> >positives.
> >
> >I am not sure if we write about the same. Please confirm, this can be
> >needed. Please.
> >
>
> Probably not. Masahiko-san pointed out that pageinspect (which also has
> a function to print pages from a BRIN index) does not understand the
> summary of the new opclasses and just prints the bytea verbatim.
>
> That has nothing to do with inspecting the bloom filter, or anything
> like that. So I think there's some confusion ...
>
>
> regards
>
> --
> Tomas Vondra                  http://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-12 23:58                                                                             ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-07-12 23:58 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On 2020-Jul-10, Tomas Vondra wrote:

> > postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
> > 2), 'mul');
> > -[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------
> > -----------------------------------------------------------------------------------------------------------------------------------
> > ----------------------------
> > itemoffset  | 1
> > blknum      | 0
> > attnum      | 1
> > allnulls    | f
> > hasnulls    | f
> > placeholder | f
> > value       | {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
> > 700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
> > 00000710000}
> 
> Hmm. I'm not sure we can do much better, without making the function
> much more complicated. I mean, even with regular BRIN indexes we don't
> really know if the value is plain min/max, right?

Maybe we can try to handle this with some other function that interprets
the bytea in 'value' and returns a user-readable text.  I think it'd
have to be a superuser-only function, because otherwise you could easily
cause a crash by passing a value of a different opclass.  But since this
seems a developer-only thing, that restriction seems fine to me.

(I don't know what's a good way to represent a bloom filter, mind.)

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-07-13 00:14                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-13 00:14 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Sun, Jul 12, 2020 at 07:58:54PM -0400, Alvaro Herrera wrote:
>On 2020-Jul-10, Tomas Vondra wrote:
>
>> > postgres(1:12801)=# select * from brin_page_items(get_raw_page('mul',
>> > 2), 'mul');
>> > -[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------
>> > -----------------------------------------------------------------------------------------------------------------------------------
>> > ----------------------------
>> > itemoffset  | 1
>> > blknum      | 0
>> > attnum      | 1
>> > allnulls    | f
>> > hasnulls    | f
>> > placeholder | f
>> > value       | {\x010000001b0000002000000001000000e5700000e6700000e7700000e8700000e9700000ea700000eb700000ec700000ed700000ee700000ef
>> > 700000f0700000f1700000f2700000f3700000f4700000f5700000f6700000f7700000f8700000f9700000fa700000fb700000fc700000fd700000fe700000ff700
>> > 00000710000}
>>
>> Hmm. I'm not sure we can do much better, without making the function
>> much more complicated. I mean, even with regular BRIN indexes we don't
>> really know if the value is plain min/max, right?
>
>Maybe we can try to handle this with some other function that interprets
>the bytea in 'value' and returns a user-readable text.  I think it'd
>have to be a superuser-only function, because otherwise you could easily
>cause a crash by passing a value of a different opclass.  But since this
>seems a developer-only thing, that restriction seems fine to me.
>

Ummm, I disagree a superuser check is sufficient protection from a
segfault or similar issues. If we really want to print something nicer,
I'd say it needs to be a special function in the BRIN opclass.

>(I don't know what's a good way to represent a bloom filter, mind.)
>

Me neither, but I guess we could print either some stats (size, number
of bits set, etc.) and/or then the bitmap.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-13 00:33                                                                                 ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-07-13 00:33 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On 2020-Jul-13, Tomas Vondra wrote:

> On Sun, Jul 12, 2020 at 07:58:54PM -0400, Alvaro Herrera wrote:

> > Maybe we can try to handle this with some other function that interprets
> > the bytea in 'value' and returns a user-readable text.  I think it'd
> > have to be a superuser-only function, because otherwise you could easily
> > cause a crash by passing a value of a different opclass.  But since this
> > seems a developer-only thing, that restriction seems fine to me.
> 
> Ummm, I disagree a superuser check is sufficient protection from a
> segfault or similar issues.

My POV there is that it's the user's responsibility to call the right
function; and if they fail to do so, it's their fault.  I agree it's not
ideal, but frankly these pageinspect things are not critical to get 100%
user-friendly.

> If we really want to print something nicer, I'd say it needs to be a
> special function in the BRIN opclass.

If that can be done, then +1.  We just need to ensure that the function
knows and can verify the type of index that the value comes from.  I
guess we can pass the index OID so that it can extract the opclass from
catalogs to verify.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-07-13 05:54                                                                                   ` Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Masahiko Sawada @ 2020-07-13 05:54 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Mon, 13 Jul 2020 at 09:33, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
>
> On 2020-Jul-13, Tomas Vondra wrote:
>
> > On Sun, Jul 12, 2020 at 07:58:54PM -0400, Alvaro Herrera wrote:
>
> > > Maybe we can try to handle this with some other function that interprets
> > > the bytea in 'value' and returns a user-readable text.  I think it'd
> > > have to be a superuser-only function, because otherwise you could easily
> > > cause a crash by passing a value of a different opclass.  But since this
> > > seems a developer-only thing, that restriction seems fine to me.
> >
> > Ummm, I disagree a superuser check is sufficient protection from a
> > segfault or similar issues.
>
> My POV there is that it's the user's responsibility to call the right
> function; and if they fail to do so, it's their fault.  I agree it's not
> ideal, but frankly these pageinspect things are not critical to get 100%
> user-friendly.
>
> > If we really want to print something nicer, I'd say it needs to be a
> > special function in the BRIN opclass.
>
> If that can be done, then +1.  We just need to ensure that the function
> knows and can verify the type of index that the value comes from.  I
> guess we can pass the index OID so that it can extract the opclass from
> catalogs to verify.

+1 from me, too. Perhaps we can have it as optional. If a BRIN opclass
doesn't have it, the 'values' can be null.

Regards,

-- 
Masahiko Sawada            http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
@ 2020-07-13 14:58                                                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-13 14:58 UTC (permalink / raw)
  To: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Mon, Jul 13, 2020 at 02:54:56PM +0900, Masahiko Sawada wrote:
>On Mon, 13 Jul 2020 at 09:33, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
>>
>> On 2020-Jul-13, Tomas Vondra wrote:
>>
>> > On Sun, Jul 12, 2020 at 07:58:54PM -0400, Alvaro Herrera wrote:
>>
>> > > Maybe we can try to handle this with some other function that interprets
>> > > the bytea in 'value' and returns a user-readable text.  I think it'd
>> > > have to be a superuser-only function, because otherwise you could easily
>> > > cause a crash by passing a value of a different opclass.  But since this
>> > > seems a developer-only thing, that restriction seems fine to me.
>> >
>> > Ummm, I disagree a superuser check is sufficient protection from a
>> > segfault or similar issues.
>>
>> My POV there is that it's the user's responsibility to call the right
>> function; and if they fail to do so, it's their fault.  I agree it's not
>> ideal, but frankly these pageinspect things are not critical to get 100%
>> user-friendly.
>>
>> > If we really want to print something nicer, I'd say it needs to be a
>> > special function in the BRIN opclass.
>>
>> If that can be done, then +1.  We just need to ensure that the function
>> knows and can verify the type of index that the value comes from.  I
>> guess we can pass the index OID so that it can extract the opclass from
>> catalogs to verify.
>
>+1 from me, too. Perhaps we can have it as optional. If a BRIN opclass
>doesn't have it, the 'values' can be null.
>

I'd say that if the opclass does not have it, then we should print the
bytea value (or whatever the opclass uses to store the summary) using
the type functions.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-07-15 02:34                                                                                       ` Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alexander Korotkov @ 2020-07-15 02:34 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Mon, Jul 13, 2020 at 5:59 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> >> > If we really want to print something nicer, I'd say it needs to be a
> >> > special function in the BRIN opclass.
> >>
> >> If that can be done, then +1.  We just need to ensure that the function
> >> knows and can verify the type of index that the value comes from.  I
> >> guess we can pass the index OID so that it can extract the opclass from
> >> catalogs to verify.
> >
> >+1 from me, too. Perhaps we can have it as optional. If a BRIN opclass
> >doesn't have it, the 'values' can be null.
> >
>
> I'd say that if the opclass does not have it, then we should print the
> bytea value (or whatever the opclass uses to store the summary) using
> the type functions.

I've read the recent messages in this thread and I'd like to share my thoughts.

I think the way brin_page_items() displays values is not really
generic.  It uses a range-like textual representation of an array of
values, while that array doesn't necessarily have range semantics.

However, I think it's good that brin_page_items() uses a type output
function to display values.  So, it's not necessary to introduce a new
BRIN opclass function in order to get values displayed in a
human-readable way.  Instead, we could just make a standard of BRIN
value to be human readable.  I see at least two possibilities for
that.
1. Use standard container data-types to represent BRIN values.  For
instance we could use an array of ranges instead of bytea for
multirange.  Not about how convenient/performant it would be.
2. Introduce new data-type to represent values in BRIN index. And for
that type we can define output function with user-readable output. We
did similar things for GiST.  For instance, pg_trgm defines gtrgm
type, which has no input and no output. But for BRIN opclass we can
define type with just output.

BTW, I've applied the patchset to the current master, but I got a lot
of duplicate oids.  Could you please resolve these conflicts.  I think
it would be good to use high oid numbers to evade conflicts during
development/review, and rely on committer to set final oids (as
discussed in [1]).

Links
1. https://www.postgresql.org/message-id/CAH2-WzmMTGMcPuph4OvsO7Ykut0AOCF_i-%3DeaochT0dd2BN9CQ%40mail.g...

------
Regards,
Alexander Korotkov





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
@ 2020-07-19 15:19                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-07-19 15:19 UTC (permalink / raw)
  To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Wed, Jul 15, 2020 at 05:34:05AM +0300, Alexander Korotkov wrote:
>On Mon, Jul 13, 2020 at 5:59 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> >> > If we really want to print something nicer, I'd say it needs to be a
>> >> > special function in the BRIN opclass.
>> >>
>> >> If that can be done, then +1.  We just need to ensure that the function
>> >> knows and can verify the type of index that the value comes from.  I
>> >> guess we can pass the index OID so that it can extract the opclass from
>> >> catalogs to verify.
>> >
>> >+1 from me, too. Perhaps we can have it as optional. If a BRIN opclass
>> >doesn't have it, the 'values' can be null.
>> >
>>
>> I'd say that if the opclass does not have it, then we should print the
>> bytea value (or whatever the opclass uses to store the summary) using
>> the type functions.
>
>I've read the recent messages in this thread and I'd like to share my thoughts.
>
>I think the way brin_page_items() displays values is not really
>generic.  It uses a range-like textual representation of an array of
>values, while that array doesn't necessarily have range semantics.
>
>However, I think it's good that brin_page_items() uses a type output
>function to display values.  So, it's not necessary to introduce a new
>BRIN opclass function in order to get values displayed in a
>human-readable way.  Instead, we could just make a standard of BRIN
>value to be human readable.  I see at least two possibilities for
>that.
>1. Use standard container data-types to represent BRIN values.  For
>instance we could use an array of ranges instead of bytea for
>multirange.  Not about how convenient/performant it would be.
>2. Introduce new data-type to represent values in BRIN index. And for
>that type we can define output function with user-readable output. We
>did similar things for GiST.  For instance, pg_trgm defines gtrgm
>type, which has no input and no output. But for BRIN opclass we can
>define type with just output.
>

I think there's a number of weak points in this approach.

Firstly, it assumes the summaries can be represented as arrays of
built-in types, which I'm not really sure about. It clearly is not true
for the bloom opclasses, for example. But even for minmax oclasses it's
going to be tricky because the ranges may be on different data types so
presumably we'd need somewhat nested data structure.

Moreover, multi-minmax summary contains either points or intervals,
which requires additional fields/flags to indicate that. That further
complicates the things ...

maybe we could decompose that into separate arrays or something, but
honestly it seems somewhat premature - there are far more important
aspects to discuss, I think (e.g. how the ranges are built/merged in
multi-minmax, or whether bloom opclasses are useful at all).


>BTW, I've applied the patchset to the current master, but I got a lot
>of duplicate oids.  Could you please resolve these conflicts.  I think
>it would be good to use high oid numbers to evade conflicts during
>development/review, and rely on committer to set final oids (as
>discussed in [1]).
>
>Links
>1. https://www.postgresql.org/message-id/CAH2-WzmMTGMcPuph4OvsO7Ykut0AOCF_i-%3DeaochT0dd2BN9CQ%40mail.g...
>

Did you use the patchset from 2020/07/03? I don't get any duplicate OIDs
with it, and it's already using quite high OIDs (part 4 uses >= 8000,
part 5 uses >= 9000).

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-08-04 14:36                                                                                           ` Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alexander Korotkov @ 2020-08-04 14:36 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

Hi, Tomas!

Sorry for the late reply.

On Sun, Jul 19, 2020 at 6:19 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> I think there's a number of weak points in this approach.
>
> Firstly, it assumes the summaries can be represented as arrays of
> built-in types, which I'm not really sure about. It clearly is not true
> for the bloom opclasses, for example. But even for minmax oclasses it's
> going to be tricky because the ranges may be on different data types so
> presumably we'd need somewhat nested data structure.
>
> Moreover, multi-minmax summary contains either points or intervals,
> which requires additional fields/flags to indicate that. That further
> complicates the things ...
>
> maybe we could decompose that into separate arrays or something, but
> honestly it seems somewhat premature - there are far more important
> aspects to discuss, I think (e.g. how the ranges are built/merged in
> multi-minmax, or whether bloom opclasses are useful at all).

I see.  But there is at least a second option to introduce a new
datatype with just an output function.  In the similar way
gist/tsvector_ops uses gtsvector key type.  I think it would be more
transparent than using just bytea.  Also, this is the way we already
use in the core.

> >BTW, I've applied the patchset to the current master, but I got a lot
> >of duplicate oids.  Could you please resolve these conflicts.  I think
> >it would be good to use high oid numbers to evade conflicts during
> >development/review, and rely on committer to set final oids (as
> >discussed in [1]).
> >
> >Links
> >1. https://www.postgresql.org/message-id/CAH2-WzmMTGMcPuph4OvsO7Ykut0AOCF_i-%3DeaochT0dd2BN9CQ%40mail.g...
>
> Did you use the patchset from 2020/07/03? I don't get any duplicate OIDs
> with it, and it's already using quite high OIDs (part 4 uses >= 8000,
> part 5 uses >= 9000).

Yep, it appears that I was using the wrong version of patchset.
Patchset from 2020/07/03 works good on the current master.

------
Regards,
Alexander Korotkov





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
@ 2020-08-04 15:17                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-08-04 15:17 UTC (permalink / raw)
  To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Tue, Aug 04, 2020 at 05:36:51PM +0300, Alexander Korotkov wrote:
>Hi, Tomas!
>
>Sorry for the late reply.
>
>On Sun, Jul 19, 2020 at 6:19 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> I think there's a number of weak points in this approach.
>>
>> Firstly, it assumes the summaries can be represented as arrays of
>> built-in types, which I'm not really sure about. It clearly is not true
>> for the bloom opclasses, for example. But even for minmax oclasses it's
>> going to be tricky because the ranges may be on different data types so
>> presumably we'd need somewhat nested data structure.
>>
>> Moreover, multi-minmax summary contains either points or intervals,
>> which requires additional fields/flags to indicate that. That further
>> complicates the things ...
>>
>> maybe we could decompose that into separate arrays or something, but
>> honestly it seems somewhat premature - there are far more important
>> aspects to discuss, I think (e.g. how the ranges are built/merged in
>> multi-minmax, or whether bloom opclasses are useful at all).
>
>I see.  But there is at least a second option to introduce a new
>datatype with just an output function.  In the similar way
>gist/tsvector_ops uses gtsvector key type.  I think it would be more
>transparent than using just bytea.  Also, this is the way we already
>use in the core.
>

So you're proposing to have a new data types "brin_minmax_multi_summary"
and "brin_bloom_summary" (or some other names), with output functions
printing something nicer? I suppose that could work, and we could even
add pageinspect functions returning the value as raw bytea.

Good idea!

>> >BTW, I've applied the patchset to the current master, but I got a lot
>> >of duplicate oids.  Could you please resolve these conflicts.  I think
>> >it would be good to use high oid numbers to evade conflicts during
>> >development/review, and rely on committer to set final oids (as
>> >discussed in [1]).
>> >
>> >Links
>> >1. https://www.postgresql.org/message-id/CAH2-WzmMTGMcPuph4OvsO7Ykut0AOCF_i-%3DeaochT0dd2BN9CQ%40mail.g...
>>
>> Did you use the patchset from 2020/07/03? I don't get any duplicate OIDs
>> with it, and it's already using quite high OIDs (part 4 uses >= 8000,
>> part 5 uses >= 9000).
>
>Yep, it appears that I was using the wrong version of patchset.
>Patchset from 2020/07/03 works good on the current master.
>

OK, good.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-08-07 16:27                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-08-07 16:27 UTC (permalink / raw)
  To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Tom Lane <tgl@sss.pgh.pa.us>; Andres Freund <andres@anarazel.de>; Mark Dilger <hornschnorter@gmail.com>; pgsql-hackers

On Tue, Aug 04, 2020 at 05:17:43PM +0200, Tomas Vondra wrote:
>On Tue, Aug 04, 2020 at 05:36:51PM +0300, Alexander Korotkov wrote:
>>Hi, Tomas!
>>
>>Sorry for the late reply.
>>
>>On Sun, Jul 19, 2020 at 6:19 PM Tomas Vondra
>><tomas.vondra@2ndquadrant.com> wrote:
>>>I think there's a number of weak points in this approach.
>>>
>>>Firstly, it assumes the summaries can be represented as arrays of
>>>built-in types, which I'm not really sure about. It clearly is not true
>>>for the bloom opclasses, for example. But even for minmax oclasses it's
>>>going to be tricky because the ranges may be on different data types so
>>>presumably we'd need somewhat nested data structure.
>>>
>>>Moreover, multi-minmax summary contains either points or intervals,
>>>which requires additional fields/flags to indicate that. That further
>>>complicates the things ...
>>>
>>>maybe we could decompose that into separate arrays or something, but
>>>honestly it seems somewhat premature - there are far more important
>>>aspects to discuss, I think (e.g. how the ranges are built/merged in
>>>multi-minmax, or whether bloom opclasses are useful at all).
>>
>>I see.  But there is at least a second option to introduce a new
>>datatype with just an output function.  In the similar way
>>gist/tsvector_ops uses gtsvector key type.  I think it would be more
>>transparent than using just bytea.  Also, this is the way we already
>>use in the core.
>>
>
>So you're proposing to have a new data types "brin_minmax_multi_summary"
>and "brin_bloom_summary" (or some other names), with output functions
>printing something nicer? I suppose that could work, and we could even
>add pageinspect functions returning the value as raw bytea.
>
>Good idea!
>

Attached is an updated version of the patch series, implementing this.
Adding the extra data types was fairly simple, because both bloom and
minmax-multi indexes already used "struct as varlena" approach, so all
that needed was a bunch of in/out functions and catalog records.

I've left the changes in separate patches for clarity, ultimately it'll
get merged into the other parts.


This reminded me that the current costing may not quite work, because
it depends on how well the index is correlated to the table. That may
be OK for minmax-multi in most cases, but for bloom it makes almost no
sense - correlation does not really matter for bloom filters, what
matters is the number of values in each range.

Consider this example:

create table t (a int);

insert into t select x from (
   select (i/10) as x from generate_series(1,10000000) s(i)
   order by random()
) foo;

create index on t using brin(
   a int4_bloom_ops(n_distinct_per_range=6000,
                    false_positive_rate=0.05))
with (pages_per_range = 16);

vacuum analyze t;

test=# explain analyze select * from t where a = 10000;
                                              QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
  Seq Scan on t  (cost=0.00..169247.71 rows=10 width=4) (actual time=38.088..513.654 rows=10 loops=1)
    Filter: (a = 10000)
    Rows Removed by Filter: 9999990
  Planning Time: 0.060 ms
  Execution Time: 513.719 ms
(5 rows)

test=# set enable_seqscan = off;
SET
test=# explain analyze select * from t where a = 10000;
                                                          QUERY PLAN                                                         
----------------------------------------------------------------------------------------------------------------------------
  Bitmap Heap Scan on t  (cost=5553.07..174800.78 rows=10 width=4) (actual time=7.790..27.585 rows=10 loops=1)
    Recheck Cond: (a = 10000)
    Rows Removed by Index Recheck: 224182
    Heap Blocks: lossy=992
    ->  Bitmap Index Scan on t_a_idx  (cost=0.00..5553.06 rows=9999977 width=0) (actual time=7.006..7.007 rows=9920 loops=1)
          Index Cond: (a = 10000)
  Planning Time: 0.052 ms
  Execution Time: 27.658 ms
(8 rows)

Clearly, the main problem is in brincostestimate relying on correlation
to tweak the selectivity estimates, leading to an estimate of almost the
whole table, when in practice we only scan a tiny fraction.

Part 0008 is an experimental tweaks the logic to ignore correlation for
bloom and minmax-multi opclasses, producing this plan:

test=# explain analyze select * from t where a = 10000;
                                                         QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
  Bitmap Heap Scan on t  (cost=5542.01..16562.95 rows=10 width=4) (actual time=12.013..34.705 rows=10 loops=1)
    Recheck Cond: (a = 10000)
    Rows Removed by Index Recheck: 224182
    Heap Blocks: lossy=992
    ->  Bitmap Index Scan on t_a_idx  (cost=0.00..5542.00 rows=3615 width=0) (actual time=11.108..11.109 rows=9920 loops=1)
          Index Cond: (a = 10000)
  Planning Time: 0.386 ms
  Execution Time: 34.778 ms
(8 rows)

which is way closer to reality, of course. I'm not entirely sure it
behaves correctly for multi-column BRIN indexes, but I think as a PoC
it's sufficient.

For bloom, I think we can be a bit smarter - we could use the false
positive rate as the "minimum expected selectivity" or something like
that. After all, the false positive rate essentially means "Given a
random value, what's the chance that a bloom filter matches?" So given a
table with N ranges, we expect about (N * fpr) to match. Of course, the
problem is that this only works for "full" bloom filters. Ranges with
fewer distinct values will have much lower probability, and ranges with
unexpectedly many distinct values will have much higher probability.

But I think we can ignore that, assume the index was created with good
parameters, so the bloom filters won't degrade and the target fpr is
probably a defensive value.

For minmax-multi, we probably should not ignore correlation entirely.
It does handle imperfect correlation much more gracefully than plain
minmax, but it still depends on reasonably ordered data.

A possible improvement would be to compute average "covering" of ranges,
i.e. given the length of a column domain

     D = MAX(column) - MIN(column)

compute what fraction of that is covered by a range by summing lengths
of intervals in the range, and dividing it by D. And then averaging it
over all BRIN ranges.

This would allow us to estimate how many ranges are matched by a random
value from the column domain, I think. But this requires extending what
data analyze collects for indexes - I don't think there are any stats
specific to BRIN-specific collected at the moment.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (8 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-a-20200807.patch (text/plain, 20.8K) download From 6ca56a8be03680ba398728ea862f6908b164fbf1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 1/8] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..6777d48faf 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +520,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,34 +530,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -519,12 +554,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 082a11f270..018a6490fb 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8086,7 +8086,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8102,7 +8102,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200807.patch (text/plain, 9.9K) download From e70a896cd8d38c6d90c22cd9ebb9a3fb5c229660 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 2/8] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 6777d48faf..caf7b62688 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,8 +389,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +417,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -440,14 +445,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -455,9 +458,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -544,6 +561,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.25.4
0003-Move-processing-of-NULLs-from-BRIN-support--20200807.patch (text/plain, 16.1K) download From 4381cd1ff3c3a822d969d4dc4e400b4d208606aa Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 3/8] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index caf7b62688..a9c44c0b82 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -562,69 +541,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -772,7 +713,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -801,25 +741,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1598,6 +1521,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1651,3 +1607,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0004-BRIN-bloom-indexes-20200807.patch (text/plain, 126.7K) download From 104611d3dd82beafc9b15f015d9e7f84b761f1ce Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:57:59 +0200 Subject: [PATCH 4/8] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 215 +++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 ++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 +++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++++ 17 files changed, 2823 insertions(+), 2 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 55b6272db6..e10fc64d6b 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -132,6 +132,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry><literal>int8_bloom_ops</literal></entry> + <entry><type>bigint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int8_minmax_ops</literal></entry> <entry><type>bigint</type></entry> @@ -183,6 +190,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>|&amp;&gt;</literal> </entry> </row> + <row> + <entry><literal>bytea_bloom_ops</literal></entry> + <entry><type>bytea</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bytea_minmax_ops</literal></entry> <entry><type>bytea</type></entry> @@ -194,6 +208,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>bpchar_bloom_ops</literal></entry> + <entry><type>character</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> @@ -205,6 +226,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>char_bloom_ops</literal></entry> + <entry><type>"char"</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>char_minmax_ops</literal></entry> <entry><type>"char"</type></entry> @@ -216,6 +244,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_bloom_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>date_minmax_ops</literal></entry> <entry><type>date</type></entry> @@ -227,6 +262,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_bloom_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float8_minmax_ops</literal></entry> <entry><type>double precision</type></entry> @@ -238,6 +280,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_bloom_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>inet_minmax_ops</literal></entry> <entry><type>inet</type></entry> @@ -261,6 +310,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&lt;&lt;</literal> </entry> </row> + <row> + <entry><literal>int4_bloom_ops</literal></entry> + <entry><type>integer</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int4_minmax_ops</literal></entry> <entry><type>integer</type></entry> @@ -272,6 +328,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_bloom_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>interval_minmax_ops</literal></entry> <entry><type>interval</type></entry> @@ -283,6 +346,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_bloom_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr_minmax_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -294,6 +364,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_bloom_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>macaddr8_minmax_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -305,6 +382,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>name_bloom_ops</literal></entry> + <entry><type>name</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>name_minmax_ops</literal></entry> <entry><type>name</type></entry> @@ -316,6 +400,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>numeric_bloom_ops</literal></entry> + <entry><type>numeric</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>numeric_minmax_ops</literal></entry> <entry><type>numeric</type></entry> @@ -327,6 +418,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_bloom_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> @@ -338,6 +436,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>oid_bloom_ops</literal></entry> + <entry><type>oid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>oid_minmax_ops</literal></entry> <entry><type>oid</type></entry> @@ -369,6 +474,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;=</literal> </entry> </row> + <row> + <entry><literal>float4_bloom_ops</literal></entry> + <entry><type>real</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>float4_minmax_ops</literal></entry> <entry><type>real</type></entry> @@ -380,6 +492,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>int2_bloom_ops</literal></entry> + <entry><type>smallint</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>int2_minmax_ops</literal></entry> <entry><type>smallint</type></entry> @@ -391,6 +510,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>text_bloom_ops</literal></entry> + <entry><type>text</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>text_minmax_ops</literal></entry> <entry><type>text</type></entry> @@ -413,6 +539,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_bloom_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamp_minmax_ops</literal></entry> <entry><type>timestamp without time zone</type></entry> @@ -424,6 +557,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_bloom_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timestamptz_minmax_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -435,6 +575,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_bloom_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>time_minmax_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -446,6 +593,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_bloom_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>timetz_minmax_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -457,6 +611,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_bloom_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>=</literal> + </entry> + </row> <row> <entry><literal>uuid_minmax_ops</literal></entry> <entry><type>uuid</type></entry> @@ -841,6 +1002,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..b119e4e264 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,980 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + Size len; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* TODO optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (values[i] != values[nvalues - 1]) + values[nvalues++] = values[i]; + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..af6891e348 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -2002,6 +2065,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2096,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2118,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2140,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2182,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2204,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2355,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2410,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2432,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2488,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2510,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2576,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..c24a80545a 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -984,6 +1125,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1178,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1212,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1245,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1297,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1330,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1455,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1525,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1559,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1619,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1652,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1705,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..16d6111ab6 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +386,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..7f733aeab1 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 018a6490fb..5336aa7b9c 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8109,6 +8109,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 555d464f91..c1d19b1e0c 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4929,8 +4929,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-add-special-pg_brin_bloom_summary-data-type-20200807.patch (text/plain, 5.8K) download From b8ac6d8c00665ba2d2ae13777642cd35b2de4fe1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 6 Aug 2020 17:56:22 +0200 Subject: [PATCH 5/8] add special pg_brin_bloom_summary data type --- src/backend/access/brin/brin_bloom.c | 91 ++++++++++++++++++++++- src/include/catalog/pg_proc.dat | 14 ++++ src/include/catalog/pg_type.dat | 7 ++ src/test/regress/expected/type_sanity.out | 7 +- 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index b119e4e264..e7ca100821 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -647,7 +647,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); - result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); PG_RETURN_POINTER(result); } @@ -978,3 +978,92 @@ brin_bloom_options(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 5336aa7b9c..da4c9c6202 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10975,3 +10975,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, -- 2.25.4
0006-BRIN-multi-range-minmax-indexes-20200807.patch (text/plain, 198.0K) download From 1b81e264cd2e6e27f3f2149468da774cf9faa3cc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 6 Aug 2020 17:00:09 +0200 Subject: [PATCH 6/8] BRIN multi-range minmax indexes --- doc/src/sgml/brin.sgml | 212 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2178 +++++++++++++++++++ src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 545 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 65 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 ++++ 17 files changed, 4498 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index e10fc64d6b..e1be48996d 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -262,6 +262,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>date_minmax_multi_ops</literal></entry> + <entry><type>date</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>float8_bloom_ops</literal></entry> <entry><type>double precision</type></entry> @@ -280,6 +291,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>float8_minmax_multi_ops</literal></entry> + <entry><type>double precision</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>inet_bloom_ops</literal></entry> <entry><type>inet</type></entry> @@ -298,6 +320,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>inet_minmax_multi_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>network_inclusion_ops</literal></entry> <entry><type>inet</type></entry> @@ -346,6 +379,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>interval_minmax_multi_ops</literal></entry> + <entry><type>interval</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr_bloom_ops</literal></entry> <entry><type>macaddr</type></entry> @@ -364,6 +408,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><type>macaddr</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>macaddr8_bloom_ops</literal></entry> <entry><type>macaddr8</type></entry> @@ -382,6 +437,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><type>macaddr8</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>name_bloom_ops</literal></entry> <entry><type>name</type></entry> @@ -436,6 +502,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><type>pg_lsn</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>oid_bloom_ops</literal></entry> <entry><type>oid</type></entry> @@ -557,6 +634,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><type>timestamp without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timestamptz_bloom_ops</literal></entry> <entry><type>timestamp with time zone</type></entry> @@ -575,6 +663,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><type>timestamp with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>time_bloom_ops</literal></entry> <entry><type>time without time zone</type></entry> @@ -593,6 +692,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>time_minmax_multi_ops</literal></entry> + <entry><type>time without time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>timetz_bloom_ops</literal></entry> <entry><type>time with time zone</type></entry> @@ -611,6 +721,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>timetz_minmax_multi_ops</literal></entry> + <entry><type>time with time zone</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> <row> <entry><literal>uuid_bloom_ops</literal></entry> <entry><type>uuid</type></entry> @@ -629,6 +750,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <literal>&gt;</literal> </entry> </row> + <row> + <entry><literal>uuid_minmax_multi_ops</literal></entry> + <entry><type>uuid</type></entry> + <entry> + <literal>&lt;</literal> + <literal>&lt;=</literal> + <literal>=</literal> + <literal>&gt;=</literal> + <literal>&gt;</literal> + </entry> + </row> </tbody> </tgroup> </table> @@ -753,13 +885,13 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions using + them are shipped for in-core data types as appropriate. Additional operator + classes can be defined by the user for other data types using equivalent + definitions, without having to write any source code; appropriate catalog + entries being declared is enough. Note that assumptions about the semantics + of operator strategies are embedded in the support functions' source code. </para> <para> @@ -830,6 +962,72 @@ typedef struct BrinOpcInfo </tgroup> </table> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> To write an operator class for a complex data type which has values included within another type, it's possible to use the inclusion support diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..f6b8fbd1d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..9b9eedbb63 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2178 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + /* (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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (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; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + compar = FunctionCall2Coll(cmpFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + FmgrInfo *cmpFn; + Datum *values; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/transam.h b/src/include/access/transam.h index a91a0c7487..18a8fd77d9 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -130,14 +130,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -151,7 +151,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index af6891e348..31ec56766a 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1999,6 +2162,23 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2065,6 +2245,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2096,6 +2342,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2118,6 +2381,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2140,6 +2420,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2204,6 +2501,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2355,6 +2669,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2410,6 +2870,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2432,6 +2909,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2488,6 +2982,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2510,6 +3021,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2576,6 +3104,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index c24a80545a..317c9475f8 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1075,6 +1238,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1125,6 +1305,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1178,6 +1432,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1212,6 +1486,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1245,6 +1539,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1330,6 +1644,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1455,6 +1788,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1525,6 +2022,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1559,6 +2076,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1619,6 +2156,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1650,7 +2207,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1705,6 +2283,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index 16d6111ab6..92b3a577ea 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,38 +307,59 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -345,36 +375,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -386,6 +434,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -395,6 +446,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -404,6 +458,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 7f733aeab1..8bbf753f65 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,24 +220,36 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -240,10 +260,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -252,12 +276,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index da4c9c6202..59774edfa0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8092,6 +8092,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index c1d19b1e0c..05585225bc 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4926,12 +4926,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0007-add-special-pg_brin_minmax_multi_summary-da-20200807.patch (text/plain, 15.3K) download From 6a67fb31791ac4418f669e18ed5c42142e80bd6b Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 6 Aug 2020 18:20:28 +0200 Subject: [PATCH 7/8] add special pg_brin_minmax_multi_summary data type --- src/backend/access/brin/brin_minmax_multi.c | 260 +++++++++++++++++--- src/include/catalog/pg_proc.dat | 15 ++ src/include/catalog/pg_type.dat | 7 + 3 files changed, 247 insertions(+), 35 deletions(-) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 9b9eedbb63..f700700d46 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -59,6 +59,7 @@ #include "access/htup_details.h" #include "catalog/pg_type.h" #include "catalog/pg_amop.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/datum.h" @@ -165,6 +166,9 @@ typedef struct 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) */ @@ -174,11 +178,9 @@ typedef struct SerializedRanges char data[FLEXIBLE_ARRAY_MEMBER]; } SerializedRanges; -static SerializedRanges *range_serialize(Ranges *range, - AttrNumber attno, Form_pg_attribute attr); +static SerializedRanges *range_serialize(Ranges *range, Oid typid); -static Ranges *range_deserialize(SerializedRanges *range, - AttrNumber attno, Form_pg_attribute attr); +static Ranges *range_deserialize(SerializedRanges *range); /* Cache for support and strategy procesures. */ @@ -223,11 +225,13 @@ minmax_multi_init(int maxvalues) * in the in-memory value array. */ static SerializedRanges * -range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +range_serialize(Ranges *range, Oid typid) { Size len; int nvalues; SerializedRanges *serialized; + int typlen; + bool typbyval; int i; char *ptr; @@ -242,6 +246,9 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) Assert(2*range->nranges + range->nvalues <= range->maxvalues); + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + /* header is always needed */ len = offsetof(SerializedRanges,data); @@ -251,7 +258,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) * (attlen * nvalues) and we're done. For variable-length by-reference * types we need to actually walk all the values and sum the lengths. */ - if (attr->attlen == -1) /* varlena */ + if (typlen == -1) /* varlena */ { int i; for (i = 0; i < nvalues; i++) @@ -259,7 +266,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) len += VARSIZE_ANY(range->values[i]); } } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { int i; for (i = 0; i < nvalues; i++) @@ -270,8 +277,8 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) } else /* fixed-length types (even by-reference) */ { - Assert(attr->attlen > 0); - len += nvalues * attr->attlen; + Assert(typlen > 0); + len += nvalues * typlen; } /* @@ -281,6 +288,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) serialized = (SerializedRanges *) palloc0(len); SET_VARSIZE(serialized, len); + serialized->typid = typid; serialized->nranges = range->nranges; serialized->nvalues = range->nvalues; serialized->maxvalues = range->maxvalues; @@ -293,23 +301,23 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) for (i = 0; i < nvalues; i++) { - if (attr->attbyval) /* simple by-value data types */ + if (typbyval) /* simple by-value data types */ { - memcpy(ptr, &range->values[i], attr->attlen); - ptr += attr->attlen; + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; } - else if (attr->attlen > 0) /* fixed-length by-ref types */ + else if (typlen > 0) /* fixed-length by-ref types */ { - memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); - ptr += attr->attlen; + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; } - else if (attr->attlen == -1) /* varlena */ + else if (typlen == -1) /* varlena */ { int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); memcpy(ptr, DatumGetPointer(range->values[i]), tmp); ptr += tmp; } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { int tmp = strlen(DatumGetPointer(range->values[i])) + 1; memcpy(ptr, DatumGetPointer(range->values[i]), tmp); @@ -334,12 +342,13 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) * in the in-memory value array. */ static Ranges * -range_deserialize(SerializedRanges *serialized, - AttrNumber attno, Form_pg_attribute attr) +range_deserialize(SerializedRanges *serialized) { int i, nvalues; char *ptr; + bool typbyval; + int typlen; Ranges *range; @@ -358,6 +367,9 @@ range_deserialize(SerializedRanges *serialized, range->nvalues = serialized->nvalues; range->maxvalues = serialized->maxvalues; + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + /* * And now deconstruct the values into Datum array. We don't need * to copy the values and will instead just point the values to the @@ -367,23 +379,23 @@ range_deserialize(SerializedRanges *serialized, for (i = 0; i < nvalues; i++) { - if (attr->attbyval) /* simple by-value data types */ + if (typbyval) /* simple by-value data types */ { - memcpy(&range->values[i], ptr, attr->attlen); - ptr += attr->attlen; + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; } - else if (attr->attlen > 0) /* fixed-length by-ref types */ + else if (typlen > 0) /* fixed-length by-ref types */ { /* no copy, just set the value to the pointer */ range->values[i] = PointerGetDatum(ptr); - ptr += attr->attlen; + ptr += typlen; } - else if (attr->attlen == -1) /* varlena */ + else if (typlen == -1) /* varlena */ { range->values[i] = PointerGetDatum(ptr); ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { range->values[i] = PointerGetDatum(ptr); ptr += strlen(DatumGetPointer(range->values[i])) + 1; @@ -1293,7 +1305,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); - result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); PG_RETURN_POINTER(result); } @@ -1738,7 +1750,7 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) else { serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); - ranges = range_deserialize(serialized, attno, attr); + ranges = range_deserialize(serialized); } /* @@ -1749,7 +1761,7 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) if (modified) { - SerializedRanges *s = range_serialize(ranges, attno, attr); + SerializedRanges *s = range_serialize(ranges, attr->atttypid); column->bv_values[0] = PointerGetDatum(s); /* @@ -1788,13 +1800,11 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) int keyno; int rangeno; int i; - Form_pg_attribute attr; attno = column->bv_attno; - attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); - ranges = range_deserialize(serialized, attno, attr); + ranges = range_deserialize(serialized); /* inspect the ranges, and for each one evaluate the scan keys */ for (rangeno = 0; rangeno < ranges->nranges; rangeno++) @@ -1981,8 +1991,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS) serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); - ranges_a = range_deserialize(serialized_a, attno, attr); - ranges_b = range_deserialize(serialized_b, attno, attr); + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); /* make sure neither of the ranges is NULL */ Assert(ranges_a && ranges_b); @@ -2052,7 +2062,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS) /* cleanup and update the serialized value */ pfree(serialized_a); - col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attr->atttypid)); PG_RETURN_VOID(); } @@ -2176,3 +2186,183 @@ brin_minmax_multi_options(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} + diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 59774edfa0..f88e85532f 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -11054,3 +11054,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, -- 2.25.4
0008-tweak-costing-for-bloom-minmax-multi-indexe-20200807.patch (text/plain, 3.9K) download From f3b590c9a3484d3c7c5f8e36c8e92218371a6f2a Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Fri, 7 Aug 2020 15:53:55 +0200 Subject: [PATCH 8/8] tweak costing for bloom/minmax-multi indexes --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index e7ca100821..f64381b657 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -643,6 +643,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index f700700d46..5f637e0629 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1301,6 +1301,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 53d974125f..956d6f0a84 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7348,7 +7349,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7370,6 +7372,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7403,6 +7406,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7473,6 +7487,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-09 16:04                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:06                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: John Naylor @ 2020-09-09 16:04 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: pgsql-hackers

On Sat, Sep 5, 2020 at 7:21 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
>
> OK, here is a rebased version. Most of the breakage was due to changes
> to the BRIN sgml docs.

Hi Tomas,

I plan on trying some different queries on different data
distributions to get a sense of when the planner chooses a
multi-minmax index, and whether the choice is good.

Just to start, I used the artificial example in [1], but scaled down a
bit to save time. Config is at the default except for:
shared_buffers = 1GB
random_page_cost = 1.1;
effective_cache_size = 4GB;

create table t (a bigint, b int) with (fillfactor=95);

insert into t select i + 1000*random(), i+1000*random()
  from generate_series(1,10000000) s(i);

update t set a = 1, b = 1 where random() < 0.001;
update t set a = 10000000, b = 10000000 where random() < 0.001;

analyze t;

create index on t using brin (a);
CREATE INDEX
Time: 1631.452 ms (00:01.631)

explain analyze select * from t
  where a between 1923300::int and 1923600::int;

                                                        QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=16.10..43180.43 rows=291 width=12)
(actual time=217.770..1131.366 rows=288 loops=1)
   Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
   Rows Removed by Index Recheck: 9999712
   Heap Blocks: lossy=56819
   ->  Bitmap Index Scan on t_a_idx  (cost=0.00..16.03 rows=22595
width=0) (actual time=3.054..3.055 rows=568320 loops=1)
         Index Cond: ((a >= 1923300) AND (a <= 1923600))
 Planning Time: 0.328 ms
 Execution Time: 1131.411 ms
(8 rows)

Now add the multi-minmax:

create index on t using brin (a int8_minmax_multi_ops);
CREATE INDEX
Time: 6521.026 ms (00:06.521)

The first interesting thing is, with both BRIN indexes available, the
planner still chooses the conventional BRIN index. Only when I disable
it, does it choose the multi-minmax index:

explain analyze select * from t
  where a between 1923300::int and 1923600::int;

                                                       QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=68.10..43160.86 rows=291 width=12)
(actual time=1.835..4.196 rows=288 loops=1)
   Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
   Rows Removed by Index Recheck: 22240
   Heap Blocks: lossy=128
   ->  Bitmap Index Scan on t_a_idx1  (cost=0.00..68.03 rows=22523
width=0) (actual time=0.691..0.691 rows=1280 loops=1)
         Index Cond: ((a >= 1923300) AND (a <= 1923600))
 Planning Time: 0.250 ms
 Execution Time: 4.244 ms
(8 rows)

I wonder if this is a clue that something in the costing unfairly
penalizes a multi-minmax index. Maybe not enough to matter in
practice, since I wouldn't expect a user to put different kinds of
index on the same column.

The second thing is, with parallel seq scan, the query is faster than
a BRIN bitmap scan, with this pathological data distribution, but the
planner won't choose it unless forced to:

set enable_bitmapscan = 'off';
explain analyze select * from t
  where a between 1923300::int and 1923600::int;
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..120348.10 rows=291 width=12) (actual
time=372.766..380.364 rows=288 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on t  (cost=0.00..119319.00 rows=121
width=12) (actual time=268.326..366.228 rows=96 loops=3)
         Filter: ((a >= 1923300) AND (a <= 1923600))
         Rows Removed by Filter: 3333237
 Planning Time: 0.089 ms
 Execution Time: 380.434 ms
(8 rows)

And just to compare size:

BRIN         32kB
BRIN multi  136kB
Btree       188MB

[1] https://www.postgresql.org/message-id/459eef3e-48c7-0f5a-8356-992442a78bb6%402ndquadrant.com

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-09 18:06                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-09-09 18:06 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: pgsql-hackers

On Wed, Sep 09, 2020 at 12:04:28PM -0400, John Naylor wrote:
>On Sat, Sep 5, 2020 at 7:21 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>>
>> OK, here is a rebased version. Most of the breakage was due to changes
>> to the BRIN sgml docs.
>
>Hi Tomas,
>
>I plan on trying some different queries on different data
>distributions to get a sense of when the planner chooses a
>multi-minmax index, and whether the choice is good.
>
>Just to start, I used the artificial example in [1], but scaled down a
>bit to save time. Config is at the default except for:
>shared_buffers = 1GB
>random_page_cost = 1.1;
>effective_cache_size = 4GB;
>
>create table t (a bigint, b int) with (fillfactor=95);
>
>insert into t select i + 1000*random(), i+1000*random()
>  from generate_series(1,10000000) s(i);
>
>update t set a = 1, b = 1 where random() < 0.001;
>update t set a = 10000000, b = 10000000 where random() < 0.001;
>
>analyze t;
>
>create index on t using brin (a);
>CREATE INDEX
>Time: 1631.452 ms (00:01.631)
>
>explain analyze select * from t
>  where a between 1923300::int and 1923600::int;
>
>                                                        QUERY PLAN
>--------------------------------------------------------------------------------------------------------------------------
> Bitmap Heap Scan on t  (cost=16.10..43180.43 rows=291 width=12)
>(actual time=217.770..1131.366 rows=288 loops=1)
>   Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
>   Rows Removed by Index Recheck: 9999712
>   Heap Blocks: lossy=56819
>   ->  Bitmap Index Scan on t_a_idx  (cost=0.00..16.03 rows=22595
>width=0) (actual time=3.054..3.055 rows=568320 loops=1)
>         Index Cond: ((a >= 1923300) AND (a <= 1923600))
> Planning Time: 0.328 ms
> Execution Time: 1131.411 ms
>(8 rows)
>
>Now add the multi-minmax:
>
>create index on t using brin (a int8_minmax_multi_ops);
>CREATE INDEX
>Time: 6521.026 ms (00:06.521)
>
>The first interesting thing is, with both BRIN indexes available, the
>planner still chooses the conventional BRIN index. Only when I disable
>it, does it choose the multi-minmax index:
>
>explain analyze select * from t
>  where a between 1923300::int and 1923600::int;
>
>                                                       QUERY PLAN
>-------------------------------------------------------------------------------------------------------------------------
> Bitmap Heap Scan on t  (cost=68.10..43160.86 rows=291 width=12)
>(actual time=1.835..4.196 rows=288 loops=1)
>   Recheck Cond: ((a >= 1923300) AND (a <= 1923600))
>   Rows Removed by Index Recheck: 22240
>   Heap Blocks: lossy=128
>   ->  Bitmap Index Scan on t_a_idx1  (cost=0.00..68.03 rows=22523
>width=0) (actual time=0.691..0.691 rows=1280 loops=1)
>         Index Cond: ((a >= 1923300) AND (a <= 1923600))
> Planning Time: 0.250 ms
> Execution Time: 4.244 ms
>(8 rows)
>
>I wonder if this is a clue that something in the costing unfairly
>penalizes a multi-minmax index. Maybe not enough to matter in
>practice, since I wouldn't expect a user to put different kinds of
>index on the same column.
>

I think this is much more an estimation issue than a costing one. Notice
that in the "regular" BRIN minmax index we have this:

    ->  Bitmap Index Scan on t_a_idx  (cost=0.00..16.03 rows=22595
        width=0) (actual time=3.054..3.055 rows=568320 loops=1)

while for the multi-minmax we have this:

    ->  Bitmap Index Scan on t_a_idx1  (cost=0.00..68.03 rows=22523
        width=0) (actual time=0.691..0.691 rows=1280 loops=1)

So yes, the multi-minmax index is costed a bit higher, mostly because
the index is a bit larger. (There's also a tweak to the correlation, but
that does not make much difference because it's just 0.99 vs. 1.0.)

The main difference is that for minmax the bitmap index scan actually
matches ~586k rows (a bit confusing, considering the heap scan has to
process almost 10M rows during recheck). But the multi-minmax only
matches ~1300 rows, with a recheck of 22k.

I'm not sure how to consider this during costing, as we only see these
numbers at execution time. One way would be to also consider "size" of
the ranges (i.e. max-min) vs. range of the whole column. But that's not
something we already have.

I'm not sure how troublesome this issue really is - I don't think people
are very likely to have both minmax and multi-minmax indexes on the same
column.

>The second thing is, with parallel seq scan, the query is faster than
>a BRIN bitmap scan, with this pathological data distribution, but the
>planner won't choose it unless forced to:
>
>set enable_bitmapscan = 'off';
>explain analyze select * from t
>  where a between 1923300::int and 1923600::int;
>                                                      QUERY PLAN
>-----------------------------------------------------------------------------------------------------------------------
> Gather  (cost=1000.00..120348.10 rows=291 width=12) (actual
>time=372.766..380.364 rows=288 loops=1)
>   Workers Planned: 2
>   Workers Launched: 2
>   ->  Parallel Seq Scan on t  (cost=0.00..119319.00 rows=121
>width=12) (actual time=268.326..366.228 rows=96 loops=3)
>         Filter: ((a >= 1923300) AND (a <= 1923600))
>         Rows Removed by Filter: 3333237
> Planning Time: 0.089 ms
> Execution Time: 380.434 ms
>(8 rows)
>

I think this is the same root cause - the planner does not realize how
bad the minmax index actually is in this case, so it uses a bit too
optimistic estimate for costing. And then it has to do essentially
seqscan with extra work for bitmap index/heap scan.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-09 18:40                                                                                                   ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-09-09 18:40 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; pgsql-hackers

On 2020-Sep-09, John Naylor wrote:

> create index on t using brin (a);
> CREATE INDEX
> Time: 1631.452 ms (00:01.631)

> create index on t using brin (a int8_minmax_multi_ops);
> CREATE INDEX
> Time: 6521.026 ms (00:06.521)

It seems strange that the multi-minmax index takes so much longer to
build.  I wonder if there's some obvious part of the algorithm that can
be improved?

> The second thing is, with parallel seq scan, the query is faster than
> a BRIN bitmap scan, with this pathological data distribution, but the
> planner won't choose it unless forced to:
> 
> set enable_bitmapscan = 'off';
> explain analyze select * from t
>   where a between 1923300::int and 1923600::int;

This is probably explained by the fact that you likely have the whole
table in shared buffers, or at least in OS cache.  I'm not sure if the
costing should necessarily account for this.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-09-09 19:35                                                                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-09 19:35 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On Wed, Sep 09, 2020 at 03:40:41PM -0300, Alvaro Herrera wrote:
>On 2020-Sep-09, John Naylor wrote:
>
>> create index on t using brin (a);
>> CREATE INDEX
>> Time: 1631.452 ms (00:01.631)
>
>> create index on t using brin (a int8_minmax_multi_ops);
>> CREATE INDEX
>> Time: 6521.026 ms (00:06.521)
>
>It seems strange that the multi-minmax index takes so much longer to
>build.  I wonder if there's some obvious part of the algorithm that can
>be improved?
>

There are some minor optimizations possible - for example I noticed we
call minmax_multi_get_strategy_procinfo often because it happens in a
loop, and we could easily do it just once. But that saves only about 10%
or so, it's not a ground-breaking optimization.

The main reason for the slowness is that we pass the values one by one
to brin_minmax_multi_add_value - and on each call we need to deserialize
(and then sometimes also serialize) the summary, which may be quite
expensive. The regular minmax does not have this issue, it just swaps
the Datum value and that's it.

I see two possible optimizations - firstly, adding some sort of batch
variant of the add_value function, which would get a bunch of values
instead of just a single one, amortizing the serialization costs.

Another option would be to teach add_value to keep the deserialized
summary somewhere, and then force serialization at the end of the BRIN
page range. The end result would be roughly the same, I think.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-09 19:53                                                                                                       ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-09-09 19:53 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On 2020-Sep-09, Tomas Vondra wrote:

> There are some minor optimizations possible - for example I noticed we
> call minmax_multi_get_strategy_procinfo often because it happens in a
> loop, and we could easily do it just once. But that saves only about 10%
> or so, it's not a ground-breaking optimization.

Well, I guess this kind of thing should be fixed regardless while we
still know it's there, just to avoid an obvious inefficiency.

> The main reason for the slowness is that we pass the values one by one
> to brin_minmax_multi_add_value - and on each call we need to deserialize
> (and then sometimes also serialize) the summary, which may be quite
> expensive. The regular minmax does not have this issue, it just swaps
> the Datum value and that's it.

Ah, right, that's more interesting.  The original dumb BRIN code
separates BrinMemTuple from BrinTuple so that things can be operated
efficiently in memory.  Maybe something similar can be done in this
case, which also sounds like your second suggestion:

> Another option would be to teach add_value to keep the deserialized
> summary somewhere, and then force serialization at the end of the BRIN
> page range. The end result would be roughly the same, I think.


Also, I think you could get a few initial patches pushed soon, since
they look like general improvements rather than specific to multi-range.


On a differen train of thought, I wonder if we shouldn't drop the idea
of there being two minmax opclasses; just have one (still called
"minmax") and have the multi-range version be the v2 of it.  We would
still need to keep code to operate on the old one, but if you ever
REINDEX then your index is upgraded to the new one.  I see no reason to
keep the dumb minmax version around, assuming the performance is roughly
similar.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-09-09 20:26                                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-09 20:26 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On Wed, Sep 09, 2020 at 04:53:30PM -0300, Alvaro Herrera wrote:
>On 2020-Sep-09, Tomas Vondra wrote:
>
>> There are some minor optimizations possible - for example I noticed we
>> call minmax_multi_get_strategy_procinfo often because it happens in a
>> loop, and we could easily do it just once. But that saves only about 10%
>> or so, it's not a ground-breaking optimization.
>
>Well, I guess this kind of thing should be fixed regardless while we
>still know it's there, just to avoid an obvious inefficiency.
>

Sure. I was just suggesting it's not something that'd make this very
close to plain minmax opclass.

>> The main reason for the slowness is that we pass the values one by one
>> to brin_minmax_multi_add_value - and on each call we need to deserialize
>> (and then sometimes also serialize) the summary, which may be quite
>> expensive. The regular minmax does not have this issue, it just swaps
>> the Datum value and that's it.
>
>Ah, right, that's more interesting.  The original dumb BRIN code
>separates BrinMemTuple from BrinTuple so that things can be operated
>efficiently in memory.  Maybe something similar can be done in this
>case, which also sounds like your second suggestion:
>
>> Another option would be to teach add_value to keep the deserialized
>> summary somewhere, and then force serialization at the end of the BRIN
>> page range. The end result would be roughly the same, I think.
>

Well, the patch already has Ranges (memory) and SerializedRanges (disk)
but it's not very clear to me where to stash the in-memory data and
where to make the conversion.

>
>Also, I think you could get a few initial patches pushed soon, since
>they look like general improvements rather than specific to multi-range.
>

Yeah, I agree. I plan to review those once again in a couple days and
then push them.

>
>On a differen train of thought, I wonder if we shouldn't drop the idea
>of there being two minmax opclasses; just have one (still called
>"minmax") and have the multi-range version be the v2 of it.  We would
>still need to keep code to operate on the old one, but if you ever
>REINDEX then your index is upgraded to the new one.  I see no reason to
>keep the dumb minmax version around, assuming the performance is roughly
>similar.
>

I'm not a huge fan of that. I think it's unlikely we'll ever make this
new set of oplasses just as fast as the plain minmax, and moreover it
does have some additional requirements (e.g. the distance proc, which
may not make sense for some data types).


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-10 18:32                                                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-10 20:42                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-09-10 18:32 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On Wed, Sep 09, 2020 at 10:26:00PM +0200, Tomas Vondra wrote:
>On Wed, Sep 09, 2020 at 04:53:30PM -0300, Alvaro Herrera wrote:
>>On 2020-Sep-09, Tomas Vondra wrote:
>>
>>>There are some minor optimizations possible - for example I noticed we
>>>call minmax_multi_get_strategy_procinfo often because it happens in a
>>>loop, and we could easily do it just once. But that saves only about 10%
>>>or so, it's not a ground-breaking optimization.
>>
>>Well, I guess this kind of thing should be fixed regardless while we
>>still know it's there, just to avoid an obvious inefficiency.
>>
>
>Sure. I was just suggesting it's not something that'd make this very
>close to plain minmax opclass.
>
>>>The main reason for the slowness is that we pass the values one by one
>>>to brin_minmax_multi_add_value - and on each call we need to deserialize
>>>(and then sometimes also serialize) the summary, which may be quite
>>>expensive. The regular minmax does not have this issue, it just swaps
>>>the Datum value and that's it.
>>
>>Ah, right, that's more interesting.  The original dumb BRIN code
>>separates BrinMemTuple from BrinTuple so that things can be operated
>>efficiently in memory.  Maybe something similar can be done in this
>>case, which also sounds like your second suggestion:
>>
>>>Another option would be to teach add_value to keep the deserialized
>>>summary somewhere, and then force serialization at the end of the BRIN
>>>page range. The end result would be roughly the same, I think.
>>
>
>Well, the patch already has Ranges (memory) and SerializedRanges (disk)
>but it's not very clear to me where to stash the in-memory data and
>where to make the conversion.
>

I've spent a bit of time experimenting with this. My idea was to allow
keeping an "expanded" version of the summary somewhere. As the addValue
function only receives BrinValues I guess one option would be to just
add bv_mem_values field. Or do you have a better idea?

Of course, more would need to be done:

1) We'd need to also pass the right memory context (bt_context seems
like the right thing, but that's not something addValue sees now).

2) We'd also need to specify some sort of callback that serializes the
in-memory value into bt_values. That's not something addValue can do,
because it doesn't know whether it's the last value in the range etc. I
guess one option would be to add yet another support proc, but I guess a
simple callback would be enough.

I've hacked together an experimental version of this to see how much
would it help, and it reduces the duration from ~4.6s to ~3.3s. Which is
nice, but plain minmax is ~1.1s. I suppose there's room for further
improvements in compare_combine_ranges/reduce_combine_ranges and so on,
but I still think there'll always be a gap compared to plain minmax.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-10 20:01                                                                                                             ` Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-09-10 20:01 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On 2020-Sep-10, Tomas Vondra wrote:

> I've spent a bit of time experimenting with this. My idea was to allow
> keeping an "expanded" version of the summary somewhere. As the addValue
> function only receives BrinValues I guess one option would be to just
> add bv_mem_values field. Or do you have a better idea?

Maybe it's okay to pass the BrinMemTuple to the add_value function, and
keep something there.  Or maybe that's pointless and just a new field in
BrinValues is okay.

> Of course, more would need to be done:
> 
> 1) We'd need to also pass the right memory context (bt_context seems
> like the right thing, but that's not something addValue sees now).

You could use GetMemoryChunkContext() for that.

> 2) We'd also need to specify some sort of callback that serializes the
> in-memory value into bt_values. That's not something addValue can do,
> because it doesn't know whether it's the last value in the range etc. I
> guess one option would be to add yet another support proc, but I guess a
> simple callback would be enough.

Hmm.

> I've hacked together an experimental version of this to see how much
> would it help, and it reduces the duration from ~4.6s to ~3.3s. Which is
> nice, but plain minmax is ~1.1s. I suppose there's room for further
> improvements in compare_combine_ranges/reduce_combine_ranges and so on,
> but I still think there'll always be a gap compared to plain minmax.

The main reason I'm talking about desupporting plain minmax is that,
even if it's amazingly fast, it loses quite quickly in real-world cases
because of loss of correlation.  Minmax's build time is pretty much
determined by speed at which you can seqscan the table.  I don't think
we lose much if we add overhead in order to create an index that is 100x
more useful.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-09-11 10:14                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-11 10:14 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 10, 2020 at 05:01:37PM -0300, Alvaro Herrera wrote:
>On 2020-Sep-10, Tomas Vondra wrote:
>
>> I've spent a bit of time experimenting with this. My idea was to allow
>> keeping an "expanded" version of the summary somewhere. As the addValue
>> function only receives BrinValues I guess one option would be to just
>> add bv_mem_values field. Or do you have a better idea?
>
>Maybe it's okay to pass the BrinMemTuple to the add_value function, and
>keep something there.  Or maybe that's pointless and just a new field in
>BrinValues is okay.
>

OK. I don't like changing the add_value API very much, so for the
experimental version I simply added three new fields into the BrinValues
struct - the deserialized value, serialization callback and the memory
context. This seems to be working good enough for a WIP version.

With the original (non-batched) patch version a build of an index took
about 4s. With the minmax_multi_get_strategy_procinfo optimization and
batch build it now takes ~2.6s, which is quite nice. It's still ~2.5x as
much compared to plain minmax though.

I think there's still room for a bit more improvement (in how we merge
the ranges etc.) and maybe we can get to ~2s or something like that.


>> Of course, more would need to be done:
>>
>> 1) We'd need to also pass the right memory context (bt_context seems
>> like the right thing, but that's not something addValue sees now).
>
>You could use GetMemoryChunkContext() for that.
>

Maybe, although I prefer to just pass the memory context explicitly.

>> 2) We'd also need to specify some sort of callback that serializes the
>> in-memory value into bt_values. That's not something addValue can do,
>> because it doesn't know whether it's the last value in the range etc. I
>> guess one option would be to add yet another support proc, but I guess a
>> simple callback would be enough.
>
>Hmm.
>

I added a simple serialization callback. It works but it's a bit weird
that twe have most functions defined as support procedures, and then
this extra C callback.

>> I've hacked together an experimental version of this to see how much
>> would it help, and it reduces the duration from ~4.6s to ~3.3s. Which is
>> nice, but plain minmax is ~1.1s. I suppose there's room for further
>> improvements in compare_combine_ranges/reduce_combine_ranges and so on,
>> but I still think there'll always be a gap compared to plain minmax.
>
>The main reason I'm talking about desupporting plain minmax is that,
>even if it's amazingly fast, it loses quite quickly in real-world cases
>because of loss of correlation.  Minmax's build time is pretty much
>determined by speed at which you can seqscan the table.  I don't think
>we lose much if we add overhead in order to create an index that is 100x
>more useful.
>

I understand. I just feel a bit uneasy about replacing an index with
something that may or may not be better for a certain use case. I mean,
if you have data set for which regular minmax works fine, wouldn't you
be annoyed if we just switched it for something slower?


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (10 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function-a-20200911.patch (text/plain, 20.8K) download From d66724b5472fb7afc40d0b7e916b128bf675e9de Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 01/10] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..6777d48faf 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +520,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,34 +530,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -519,12 +554,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba92..925b262b60 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8104,7 +8104,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8120,7 +8120,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200911.patch (text/plain, 9.9K) download From 6692845cd0f8723c66db5d19afe5add61eb65b6c Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 02/10] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 6777d48faf..caf7b62688 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,8 +389,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +417,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -440,14 +445,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -455,9 +458,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -544,6 +561,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.25.4
0003-Move-processing-of-NULLs-from-BRIN-support--20200911.patch (text/plain, 16.1K) download From c65947755bb7d5ef9c459b57a5ac246742b6d608 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 03/10] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index caf7b62688..a9c44c0b82 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -562,69 +541,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -772,7 +713,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -801,25 +741,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1598,6 +1521,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1651,3 +1607,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0004-BRIN-bloom-indexes-20200911.patch (text/plain, 129.5K) download From a7c8e89eb5be0c83e3072c6788007ae71cbe19dc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sat, 5 Sep 2020 23:54:39 +0200 Subject: [PATCH 04/10] BRIN bloom indexes --- doc/src/sgml/brin.sgml | 173 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 165 ++++ src/include/catalog/pg_amproc.dat | 430 ++++++++++ src/include/catalog/pg_opclass.dat | 69 ++ src/include/catalog/pg_opfamily.dat | 36 + src/include/catalog/pg_proc.dat | 20 + src/test/regress/expected/brin_bloom.out | 456 +++++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++++ 17 files changed, 2781 insertions(+), 2 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..9b1d94b772 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -353,6 +447,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +461,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +475,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +489,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +503,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +898,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..b119e4e264 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,980 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, + nvalues; + Size len; + uint32 *values; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* TODO optimization: sort only the unsorted part, then merge */ + pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32); + + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (values[i] != values[nvalues - 1]) + values[nvalues++] = values[i]; + } + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..af6891e348 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -2002,6 +2065,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2096,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2118,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2140,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2182,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2204,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2355,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2410,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2432,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2488,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2510,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2576,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..c24a80545a 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -984,6 +1125,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1178,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1212,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1245,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1297,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1330,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1455,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1525,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1559,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1619,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1652,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1705,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..16d6111ab6 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,127 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +386,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..7f733aeab1 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,86 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 925b262b60..c00e4ece94 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8127,6 +8127,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-add-special-pg_brin_bloom_summary-data-type-20200911.patch (text/plain, 5.8K) download From 8b24542a86ab5ac259ac3de489e3b248de265822 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 6 Aug 2020 17:56:22 +0200 Subject: [PATCH 05/10] add special pg_brin_bloom_summary data type --- src/backend/access/brin/brin_bloom.c | 91 ++++++++++++++++++++++- src/include/catalog/pg_proc.dat | 14 ++++ src/include/catalog/pg_type.dat | 7 ++ src/test/regress/expected/type_sanity.out | 7 +- 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index b119e4e264..e7ca100821 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -647,7 +647,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); - result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); PG_RETURN_POINTER(result); } @@ -978,3 +978,92 @@ brin_bloom_options(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index c00e4ece94..dc17d4ce38 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10993,3 +10993,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, -- 2.25.4
0006-BRIN-minmax-multi-indexes-20200911.patch (text/plain, 206.7K) download From a4def85da8b00c126031668c207f6541d2f1d9a6 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 6 Sep 2020 00:10:44 +0200 Subject: [PATCH 06/10] BRIN minmax-multi indexes --- doc/src/sgml/brin.sgml | 252 ++- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2172 +++++++++++++++++++ src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 545 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 65 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 ++++ 17 files changed, 4532 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 9b1d94b772..46300dada5 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -447,6 +564,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -461,6 +587,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -475,6 +610,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -489,6 +633,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -503,6 +656,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -517,6 +679,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -649,13 +820,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -952,6 +1124,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..f6b8fbd1d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..a8ff28a98d --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2172 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + /* (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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct SerializedRanges +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* (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; + +static SerializedRanges *range_serialize(Ranges *range, + AttrNumber attno, Form_pg_attribute attr); + +static Ranges *range_deserialize(SerializedRanges *range, + AttrNumber attno, Form_pg_attribute attr); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (attr->attlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (attr->attlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(attr->attlen > 0); + len += nvalues * attr->attlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (attr->attlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized, + AttrNumber attno, Form_pg_attribute attr) +{ + int i, + nvalues; + char *ptr; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (attr->attbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, attr->attlen); + ptr += attr->attlen; + } + else if (attr->attlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += attr->attlen; + } + else if (attr->attlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (attr->attlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count_values(cranges, ncranges) <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* + * If this is the first non-null value, we need to initialize the range + * list. Otherwise just extract the existing range list from BrinValues. + */ + if (column->bv_allnulls) + { + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + column->bv_allnulls = false; + modified = true; + } + else + { + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); + + if (modified) + { + SerializedRanges *s = range_serialize(ranges, attno, attr); + column->bv_values[0] = PointerGetDatum(s); + + /* + * XXX pfree must happen after range_serialize, because the Ranges value + * may reference the original serialized value. + */ + if (serialized) + pfree(serialized); + } + + pfree(ranges); + + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + Form_pg_attribute attr; + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized, attno, attr); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a, attno, attr); + ranges_b = range_deserialize(serialized_b, attno, attr); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + + 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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..b0993f8ac5 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -165,14 +165,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index af6891e348..31ec56766a 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1999,6 +2162,23 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2065,6 +2245,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2096,6 +2342,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2118,6 +2381,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2140,6 +2420,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2204,6 +2501,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2355,6 +2669,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2410,6 +2870,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2432,6 +2909,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2488,6 +2982,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2510,6 +3021,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2576,6 +3104,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index c24a80545a..317c9475f8 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1075,6 +1238,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1125,6 +1305,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1178,6 +1432,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1212,6 +1486,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1245,6 +1539,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1330,6 +1644,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1455,6 +1788,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1525,6 +2022,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1559,6 +2076,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1619,6 +2156,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1650,7 +2207,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1705,6 +2283,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index 16d6111ab6..92b3a577ea 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,38 +307,59 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -345,36 +375,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -386,6 +434,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -395,6 +446,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -404,6 +458,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 7f733aeab1..8bbf753f65 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,24 +220,36 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -240,10 +260,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -252,12 +276,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index dc17d4ce38..1936e06537 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8110,6 +8110,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0007-add-special-pg_brin_minmax_multi_summary-da-20200911.patch (text/plain, 16.3K) download From df52a663305f1f900801925e2914a4c355177427 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 6 Aug 2020 18:20:28 +0200 Subject: [PATCH 07/10] add special pg_brin_minmax_multi_summary data type --- src/backend/access/brin/brin_minmax_multi.c | 260 +++++++++++++++++--- src/include/catalog/pg_proc.dat | 15 ++ src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/type_sanity.out | 7 +- 4 files changed, 251 insertions(+), 38 deletions(-) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index a8ff28a98d..1a1ff47223 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -59,6 +59,7 @@ #include "access/htup_details.h" #include "catalog/pg_type.h" #include "catalog/pg_amop.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/datum.h" @@ -165,6 +166,9 @@ typedef struct 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) */ @@ -174,11 +178,9 @@ typedef struct SerializedRanges char data[FLEXIBLE_ARRAY_MEMBER]; } SerializedRanges; -static SerializedRanges *range_serialize(Ranges *range, - AttrNumber attno, Form_pg_attribute attr); +static SerializedRanges *range_serialize(Ranges *range, Oid typid); -static Ranges *range_deserialize(SerializedRanges *range, - AttrNumber attno, Form_pg_attribute attr); +static Ranges *range_deserialize(SerializedRanges *range); /* Cache for support and strategy procesures. */ @@ -223,11 +225,13 @@ minmax_multi_init(int maxvalues) * in the in-memory value array. */ static SerializedRanges * -range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) +range_serialize(Ranges *range, Oid typid) { Size len; int nvalues; SerializedRanges *serialized; + int typlen; + bool typbyval; int i; char *ptr; @@ -242,6 +246,9 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) Assert(2*range->nranges + range->nvalues <= range->maxvalues); + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + /* header is always needed */ len = offsetof(SerializedRanges,data); @@ -251,7 +258,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) * (attlen * nvalues) and we're done. For variable-length by-reference * types we need to actually walk all the values and sum the lengths. */ - if (attr->attlen == -1) /* varlena */ + if (typlen == -1) /* varlena */ { int i; for (i = 0; i < nvalues; i++) @@ -259,7 +266,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) len += VARSIZE_ANY(range->values[i]); } } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { int i; for (i = 0; i < nvalues; i++) @@ -270,8 +277,8 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) } else /* fixed-length types (even by-reference) */ { - Assert(attr->attlen > 0); - len += nvalues * attr->attlen; + Assert(typlen > 0); + len += nvalues * typlen; } /* @@ -281,6 +288,7 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) serialized = (SerializedRanges *) palloc0(len); SET_VARSIZE(serialized, len); + serialized->typid = typid; serialized->nranges = range->nranges; serialized->nvalues = range->nvalues; serialized->maxvalues = range->maxvalues; @@ -293,23 +301,23 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) for (i = 0; i < nvalues; i++) { - if (attr->attbyval) /* simple by-value data types */ + if (typbyval) /* simple by-value data types */ { - memcpy(ptr, &range->values[i], attr->attlen); - ptr += attr->attlen; + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; } - else if (attr->attlen > 0) /* fixed-length by-ref types */ + else if (typlen > 0) /* fixed-length by-ref types */ { - memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen); - ptr += attr->attlen; + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; } - else if (attr->attlen == -1) /* varlena */ + else if (typlen == -1) /* varlena */ { int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); memcpy(ptr, DatumGetPointer(range->values[i]), tmp); ptr += tmp; } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { int tmp = strlen(DatumGetPointer(range->values[i])) + 1; memcpy(ptr, DatumGetPointer(range->values[i]), tmp); @@ -334,12 +342,13 @@ range_serialize(Ranges *range, AttrNumber attno, Form_pg_attribute attr) * in the in-memory value array. */ static Ranges * -range_deserialize(SerializedRanges *serialized, - AttrNumber attno, Form_pg_attribute attr) +range_deserialize(SerializedRanges *serialized) { int i, nvalues; char *ptr; + bool typbyval; + int typlen; Ranges *range; @@ -358,6 +367,9 @@ range_deserialize(SerializedRanges *serialized, range->nvalues = serialized->nvalues; range->maxvalues = serialized->maxvalues; + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + /* * And now deconstruct the values into Datum array. We don't need * to copy the values and will instead just point the values to the @@ -367,23 +379,23 @@ range_deserialize(SerializedRanges *serialized, for (i = 0; i < nvalues; i++) { - if (attr->attbyval) /* simple by-value data types */ + if (typbyval) /* simple by-value data types */ { - memcpy(&range->values[i], ptr, attr->attlen); - ptr += attr->attlen; + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; } - else if (attr->attlen > 0) /* fixed-length by-ref types */ + else if (typlen > 0) /* fixed-length by-ref types */ { /* no copy, just set the value to the pointer */ range->values[i] = PointerGetDatum(ptr); - ptr += attr->attlen; + ptr += typlen; } - else if (attr->attlen == -1) /* varlena */ + else if (typlen == -1) /* varlena */ { range->values[i] = PointerGetDatum(ptr); ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); } - else if (attr->attlen == -2) /* cstring */ + else if (typlen == -2) /* cstring */ { range->values[i] = PointerGetDatum(ptr); ptr += strlen(DatumGetPointer(range->values[i])) + 1; @@ -1287,7 +1299,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); - result->oi_typcache[0] = lookup_type_cache(BYTEAOID, 0); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); PG_RETURN_POINTER(result); } @@ -1732,7 +1744,7 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) else { serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); - ranges = range_deserialize(serialized, attno, attr); + ranges = range_deserialize(serialized); } /* @@ -1743,7 +1755,7 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) if (modified) { - SerializedRanges *s = range_serialize(ranges, attno, attr); + SerializedRanges *s = range_serialize(ranges, attr->atttypid); column->bv_values[0] = PointerGetDatum(s); /* @@ -1782,13 +1794,11 @@ brin_minmax_multi_consistent(PG_FUNCTION_ARGS) int keyno; int rangeno; int i; - Form_pg_attribute attr; attno = column->bv_attno; - attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); - ranges = range_deserialize(serialized, attno, attr); + ranges = range_deserialize(serialized); /* inspect the ranges, and for each one evaluate the scan keys */ for (rangeno = 0; rangeno < ranges->nranges; rangeno++) @@ -1975,8 +1985,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS) serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); - ranges_a = range_deserialize(serialized_a, attno, attr); - ranges_b = range_deserialize(serialized_b, attno, attr); + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); /* make sure neither of the ranges is NULL */ Assert(ranges_a && ranges_b); @@ -2046,7 +2056,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS) /* cleanup and update the serialized value */ pfree(serialized_a); - col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attno, attr)); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attr->atttypid)); PG_RETURN_VOID(); } @@ -2170,3 +2180,183 @@ brin_minmax_multi_options(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} + diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1936e06537..77735c3abe 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -11072,3 +11072,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, -- 2.25.4
0008-tweak-costing-for-bloom-minmax-multi-indexe-20200911.patch (text/plain, 3.9K) download From e6feeb0f9459a876510238579aff7cc4a0ebc400 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Fri, 7 Aug 2020 15:53:55 +0200 Subject: [PATCH 08/10] tweak costing for bloom/minmax-multi indexes --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index e7ca100821..f64381b657 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -643,6 +643,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 1a1ff47223..23a8751b1f 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1295,6 +1295,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0009-WIP-batch-build-20200911.patch (text/plain, 6.8K) download From 565d9c53858d3550e282f070f9d8e92b582535ed Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 10 Sep 2020 19:58:12 +0200 Subject: [PATCH 09/10] WIP: batch build --- src/backend/access/brin/brin_minmax_multi.c | 58 ++++++++++++++------- src/backend/access/brin/brin_tuple.c | 17 ++++++ src/include/access/brin_tuple.h | 4 ++ 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 23a8751b1f..7450bffc11 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -145,6 +145,8 @@ typedef struct MinMaxOptions */ typedef struct Ranges { + 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) */ @@ -178,7 +180,7 @@ typedef struct SerializedRanges char data[FLEXIBLE_ARRAY_MEMBER]; } SerializedRanges; -static SerializedRanges *range_serialize(Ranges *range, Oid typid); +static SerializedRanges *range_serialize(Ranges *range); static Ranges *range_deserialize(SerializedRanges *range); @@ -225,11 +227,12 @@ minmax_multi_init(int maxvalues) * in the in-memory value array. */ static SerializedRanges * -range_serialize(Ranges *range, Oid typid) +range_serialize(Ranges *range) { Size len; int nvalues; SerializedRanges *serialized; + Oid typid; int typlen; bool typbyval; @@ -246,6 +249,7 @@ range_serialize(Ranges *range, Oid typid) Assert(2*range->nranges + range->nvalues <= range->maxvalues); + typid = range->typid; typbyval = get_typbyval(typid); typlen = get_typlen(typid); @@ -366,6 +370,7 @@ range_deserialize(SerializedRanges *serialized) range->nranges = serialized->nranges; range->nvalues = serialized->nvalues; range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; typbyval = get_typbyval(serialized->typid); typlen = get_typlen(serialized->typid); @@ -1699,6 +1704,14 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(delta); } +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + static int brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) { @@ -1732,20 +1745,43 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + /* use the already deserialized value, is 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. */ if (column->bv_allnulls) { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + column->bv_allnulls = false; modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; } - else + else if (!ranges) { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); } /* @@ -1754,20 +1790,6 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS) */ modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval); - if (modified) - { - SerializedRanges *s = range_serialize(ranges, attr->atttypid); - column->bv_values[0] = PointerGetDatum(s); - - /* - * XXX pfree must happen after range_serialize, because the Ranges value - * may reference the original serialized value. - */ - if (serialized) - pfree(serialized); - } - - pfree(ranges); PG_RETURN_BOOL(modified); } @@ -2057,7 +2079,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS) /* cleanup and update the serialized value */ pfree(serialized_a); - col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a, attr->atttypid)); + col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a)); PG_RETURN_VOID(); } diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* -- 2.25.4
0010-WIP-simplify-reduce_combine_ranges-20200911.patch (text/plain, 1.5K) download From 830cc627bd9359a5d0f5b18e8772147575a70426 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Fri, 11 Sep 2020 10:57:46 +0200 Subject: [PATCH 10/10] WIP: simplify reduce_combine_ranges --- src/backend/access/brin/brin_minmax_multi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 7450bffc11..c8f36b0c8a 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1066,6 +1066,7 @@ reduce_combine_ranges(CombineRange *cranges, int ncranges, { int i; int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); /* * We have one fewer 'gaps' than the ranges. We'll be decrementing @@ -1077,7 +1078,7 @@ reduce_combine_ranges(CombineRange *cranges, int ncranges, int j; int shortest; - if (count_values(cranges, ncranges) <= max_values * 0.75) + if (count <= max_values * 0.75) break; shortest = distances[i].index; @@ -1089,6 +1090,11 @@ reduce_combine_ranges(CombineRange *cranges, int ncranges, */ Assert(shortest < (ncranges - 1)); + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + /* * Move the values to join the two selected ranges. The new range is * definiely not collapsed but a regular range. -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-11 14:08                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-11 14:08 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 11, 2020 at 6:14 AM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:

> I understand. I just feel a bit uneasy about replacing an index with
> something that may or may not be better for a certain use case. I mean,
> if you have data set for which regular minmax works fine, wouldn't you
> be annoyed if we just switched it for something slower?

How about making multi minmax the default for new indexes, and those
who know their data will stay very well correlated can specify simple
minmax ops for speed? Upgraded indexes would stay the same, and only
new ones would have the risk of slowdown if not attended to.

Also, I wonder if the slowdown in building a new index is similar to
the slowdown for updates. I'd like to run some TCP-H tests (that will
take some time).

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-11 18:05                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-11 18:05 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 11, 2020 at 10:08:15AM -0400, John Naylor wrote:
>On Fri, Sep 11, 2020 at 6:14 AM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>
>> I understand. I just feel a bit uneasy about replacing an index with
>> something that may or may not be better for a certain use case. I mean,
>> if you have data set for which regular minmax works fine, wouldn't you
>> be annoyed if we just switched it for something slower?
>
>How about making multi minmax the default for new indexes, and those
>who know their data will stay very well correlated can specify simple
>minmax ops for speed? Upgraded indexes would stay the same, and only
>new ones would have the risk of slowdown if not attended to.
>

That might work, I think. I like that it's an explicit choice, i.e. we
may change what the default opclass is, but the behavior won't change
unexpectedly during REINDEX etc. It might still be a bit surprising
after dump/restore, but that's probably fine.

It would be ideal if the opclasses were binary compatible, allowing a
more seamless transition. Unfortunately that seems impossible, because
plain minmax uses two Datums to store the range, while multi-minmax uses
a more complex structure.

>Also, I wonder if the slowdown in building a new index is similar to
>the slowdown for updates. I'd like to run some TCP-H tests (that will
>take some time).
>

It might be, because it needs to deserialize/serialize the summary too,
and there's no option to amortize the costs over many inserts. OTOH the
insert probably needs to do various other things, so maybe it's won't be
that bad. But yeah, testing and benchmarking it would be nice. Do you
plan to test just the minmax-multi opclass, or will you look at the
bloom one too?

Attached is a slightly improved version - I've merged the various pieces
into the "main" patches, and made some minor additional optimizations.
I've left the cost tweak as a separate part for now, though.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (6 files, .tar.gz)

0001-Pass-all-keys-to-BRIN-consistent-function--20200911b.patch (text/plain, 20.8K) download From 294da08cef9a67fcae2b305f0759161ba24465a0 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Fri, 13 Sep 2019 18:34:39 +0200 Subject: [PATCH 1/6] Pass all keys to BRIN consistent function at once --- src/backend/access/brin/brin.c | 126 ++++++++++++----- src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 116 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 293 insertions(+), 121 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..6777d48faf 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,53 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +520,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,34 +530,19 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (!nkeys[attno - 1]) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page @@ -519,12 +554,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * the range as a whole, so break out of the loop as soon * as a false return value is obtained. */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* Check keys one by one */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..8968886ff5 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,103 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool matches; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = inclusion_consistent_key(bdesc, column, key, colloid); + + if (!matches) + break; + } + + PG_RETURN_BOOL(matches); +} + +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +376,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +437,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +458,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +477,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +512,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +522,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..1219a3a2ab 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,99 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); Datum matches; - FmgrInfo *finfo; + int keyno; + bool regular_keys = false; - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + matches = minmax_consistent_key(bdesc, column, key, colloid); + + /* found non-matching key */ + if (!matches) + break; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(matches); +} + +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +283,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba92..925b262b60 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8104,7 +8104,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8120,7 +8120,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200911b.patch (text/plain, 9.9K) download From 9728d0451731ee93e2eb9bb5982778e962253671 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sun, 9 Jun 2019 22:05:39 +0200 Subject: [PATCH 2/6] Move IS NOT NULL checks to bringetbitmap --- src/backend/access/brin/brin.c | 116 ++++++++++++++++++++--- src/backend/access/brin/brin_inclusion.c | 62 +----------- src/backend/access/brin/brin_minmax.c | 62 +----------- 3 files changed, 109 insertions(+), 131 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 6777d48faf..caf7b62688 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,8 +389,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +417,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -440,14 +445,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) keyattno - 1)->attcollation)); /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -455,9 +458,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -544,6 +561,83 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, and + * if we're violating them. In that case we can terminate + * early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + { + ScanKey key = nullkeys[attno - 1][keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* interrupt the loop as soon as we find a mismatch */ + if (!addrange) + break; + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (key->sk_flags & SK_SEARCHNULL) + { + if (!bval->bv_allnulls && !bval->bv_hasnulls) + addrange = false; + + continue; + } + + /* + * For IS NOT NULL, we can only skip ranges that are + * known to have only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (bval->bv_allnulls) + addrange = false; + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all + * indexable operators are strict and thus return false + * with NULL value in the scan key. + */ + addrange = false; + } + } + + /* + * If any of the IS [NOT] NULL keys failed, the page range as + * a whole can't pass. So terminate the loop. + */ + if (!addrange) + break; + + /* + * So either there are no IS [NOT] NULL keys, or all passed. If + * there are no regular scan keys, we're done - the page range + * matches. If there are regular keys, but the page range is + * marked as 'all nulls' it can't possibly pass (we're assuming + * the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 8968886ff5..22edc6b46f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); int keyno; bool matches; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -333,9 +276,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = inclusion_consistent_key(bdesc, column, key, colloid); diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1219a3a2ab..7a7bd21cec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) Oid colloid = PG_GET_COLLATION(); Datum matches; int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); matches = true; @@ -217,9 +160,8 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); matches = minmax_consistent_key(bdesc, column, key, colloid); -- 2.25.4
0003-Move-processing-of-NULLs-from-BRIN-support-20200911b.patch (text/plain, 16.1K) download From 60a582baa498e7fe1befaa6a37063c5bccabc334 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 2 Apr 2020 02:56:00 +0200 Subject: [PATCH 3/6] Move processing of NULLs from BRIN support functions --- src/backend/access/brin/brin.c | 260 ++++++++++++++--------- src/backend/access/brin/brin_inclusion.c | 44 +--- src/backend/access/brin/brin_minmax.c | 41 +--- src/include/access/brin_internal.h | 3 + 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index caf7b62688..a9c44c0b82 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -562,69 +541,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) (nkeys[attno - 1] <= scan->numberOfKeys)); /* - * First check if there are any IS [NOT] NULL scan keys, and - * if we're violating them. In that case we can terminate - * early, without invoking the support function. + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. * * As there may be more keys, we can only detemine mismatch * within this loop. */ - for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++) + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) { - ScanKey key = nullkeys[attno - 1][keyno]; - - Assert(key->sk_attno == bval->bv_attno); - - /* interrupt the loop as soon as we find a mismatch */ - if (!addrange) - break; - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - /* IS NULL scan key, but range has no NULLs */ - if (key->sk_flags & SK_SEARCHNULL) - { - if (!bval->bv_allnulls && !bval->bv_hasnulls) - addrange = false; - - continue; - } - - /* - * For IS NOT NULL, we can only skip ranges that are - * known to have only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (bval->bv_allnulls) - addrange = false; - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all - * indexable operators are strict and thus return false - * with NULL value in the scan key. - */ - addrange = false; - } - } - - /* - * If any of the IS [NOT] NULL keys failed, the page range as - * a whole can't pass. So terminate the loop. - */ - if (!addrange) + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; break; + } /* - * So either there are no IS [NOT] NULL keys, or all passed. If - * there are no regular scan keys, we're done - the page range - * matches. If there are regular keys, but the page range is - * marked as 'all nulls' it can't possibly pass (we're assuming - * the operators are strict). + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). */ /* No regular scan keys - page range as a whole passes. */ @@ -772,7 +713,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -801,25 +741,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1598,6 +1521,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1651,3 +1607,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 22edc6b46f..59503b6f68 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -510,37 +500,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 7a7bd21cec..8882eec12c 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -245,34 +235,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0004-BRIN-bloom-indexes-20200911b.patch (text/plain, 137.4K) download From c4a0815938be1fd5d4cabd85bfb363d254eb7b4e Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sat, 5 Sep 2020 23:54:39 +0200 Subject: [PATCH 4/6] BRIN bloom indexes add special pg_brin_bloom_summary data type do merge sort in bloom_compact --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..4e35bb3459 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -934,6 +1075,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -984,6 +1142,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1195,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1229,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1262,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1314,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1347,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1472,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1542,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1576,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1636,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1669,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1722,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 925b262b60..dc17d4ce38 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8127,6 +8127,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10973,3 +10993,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-BRIN-minmax-multi-indexes-20200911b.patch (text/plain, 217.4K) download From 534ae347fc015d6aa624315be8016f5416ebb2f8 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 6 Sep 2020 00:10:44 +0200 Subject: [PATCH 5/6] BRIN minmax-multi indexes add special pg_brin_minmax_multi_summary data type WIP: batch build WIP: simplify reduce_combine_ranges --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2390 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 8 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4796 insertions(+), 22 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..f6b8fbd1d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 64. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..227927fcf5 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2390 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} + diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..b0993f8ac5 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -165,14 +165,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDS 13000-16383 are reserved for assignment during initdb + * using the OID generator. (We start the generator at 13000.) * * OIDs beginning at 16384 are assigned from the OID generator * during normal multiuser operation. (We force the generator up to * 16384 as soon as we are in normal operation.) * - * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be + * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be * moved if we run low on OIDs in any category. Changing the macros below, * and updating relevant documentation (see bki.sgml and RELEASE_CHANGES), * should be sufficient to do this. Moving the 16384 boundary between @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 4e35bb3459..082fbe10b5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1092,6 +1255,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1142,6 +1322,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1195,6 +1449,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1229,6 +1503,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1262,6 +1556,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1347,6 +1661,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1472,6 +1805,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1542,6 +2039,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1576,6 +2093,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1636,6 +2173,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1667,7 +2224,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1722,6 +2300,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index dc17d4ce38..77735c3abe 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8110,6 +8110,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11007,3 +11072,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0006-tweak-costing-for-bloom-minmax-multi-index-20200911b.patch (text/plain, 3.9K) download From 2da736b9b73d6c048e707c473070725bd642bb8d Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Fri, 7 Aug 2020 15:53:55 +0200 Subject: [PATCH 6/6] tweak costing for bloom/minmax-multi indexes --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index c2cbbd9400..03e9d4b713 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 227927fcf5..c8f36b0c8a 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-11 19:19                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-11 19:19 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 11, 2020 at 2:05 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> that bad. But yeah, testing and benchmarking it would be nice. Do you
> plan to test just the minmax-multi opclass, or will you look at the
> bloom one too?

Yeah, I'll start looking at bloom next week, and I'll include it when
I do perf testing.

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-12 16:13                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-12 16:13 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 11, 2020 at 03:19:58PM -0400, John Naylor wrote:
>On Fri, Sep 11, 2020 at 2:05 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> that bad. But yeah, testing and benchmarking it would be nice. Do you
>> plan to test just the minmax-multi opclass, or will you look at the
>> bloom one too?
>
>Yeah, I'll start looking at bloom next week, and I'll include it when
>I do perf testing.
>

OK. Here is a slightly improved version of the patch series, with better
commit messages and comments, and with the two patches tweaking handling
of NULL values merged into one.

As mentioned in my reply to Alvaro, I'm hoping to get the first two
parts (general improvements) committed soon, so that we can focus on the
new opclasses. I now recall why I was postponing pushing those parts
because it's primarily "just" a preparation for the new opclasses. Both
the scan keys and NULL handling tweaks are not going to help existing
opclasses very much, I think.

The NULL-handling might help a bit, but the scan key changes are mostly
irrelevant. So I'm wondering if we should even change the two existing
opclasses, instead of keeping them as they are (the code actually
supports that by checking number of arguments of the constitent
function). Opinions?


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (5 files, .tar.gz)

0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20200912.patch (text/plain, 23.5K) download From 2085561ad36af6994a81c1c6001fbd636077ed38 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/5] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..a3bb3558ec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba92..925b262b60 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8104,7 +8104,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8120,7 +8120,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20200912.patch (text/plain, 23.0K) download From d7812522e57ebb89aed73af5f5d736e380a8ac1f Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:28 +0200 Subject: [PATCH 2/5] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit, and it simplifies the support procedures quite a bit, as they don't need to care about NULL values and flags at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 287 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +-------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 235 insertions(+), 264 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..83c71d0f8e 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -552,6 +551,45 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +732,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +760,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1540,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1626,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index a3bb3558ec..a625ef7aac 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0003-BRIN-bloom-indexes-20200912.patch (text/plain, 138.7K) download From 2839692af136e25083b00cba8c98f42411b05098 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 3/5] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..4e35bb3459 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -934,6 +1075,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -984,6 +1142,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1195,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1229,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1262,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1314,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1347,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1472,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1542,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1576,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1636,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1669,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1722,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 925b262b60..dc17d4ce38 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8127,6 +8127,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10973,3 +10993,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0004-BRIN-minmax-multi-indexes-20200912.patch (text/plain, 217.3K) download From 87f5f02c4c0df16715e7ca5064f22bc6c1e64f8c Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:49 +0200 Subject: [PATCH 4/5] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2390 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4793 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..d86e3ed8f2 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..227927fcf5 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2390 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} + diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 4e35bb3459..082fbe10b5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1092,6 +1255,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1142,6 +1322,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1195,6 +1449,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1229,6 +1503,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1262,6 +1556,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1347,6 +1661,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1472,6 +1805,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1542,6 +2039,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1576,6 +2093,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1636,6 +2173,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1667,7 +2224,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1722,6 +2300,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index dc17d4ce38..77735c3abe 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8110,6 +8110,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11007,3 +11072,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0005-Ignore-correlation-for-new-BRIN-opclasses-20200912.patch (text/plain, 4.1K) download From 38f2d03d252595fe007b65fc097cc688b3909931 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 5/5] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index c2cbbd9400..03e9d4b713 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 227927fcf5..c8f36b0c8a 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-13 16:40                                                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-13 16:40 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

Hi,

while running some benchmarks to see if the first two patches cause any
regressions, I found a bug in 0002 which reworks the NULL handling. The
code failed to eliminate ranges early using the IS NULL scan keys,
resulting in expensive recheck. The attached version fixes that.

I also noticed that some of the queries seem to be slightly slower, most
likely due to bringetbitmap having to split the scan keys per attribute,
which also requires some allocations etc. The regression is fairly small
might be just noise (less than 2-3% in most cases), but it seems just
allocating everything in a single chunk eliminates most of it - this is
what the new 0003 patch does.

OTOH the rework also helps in other cases - I've measured ~2-3% speedups
for cases where moving the IS NULL handling to bringetbitmap eliminates
calls to the consistent function (e.g. IS NULL queries on columns with
no NULL values).

These results seems very dependent on the hardware (especially CPU),
though, and the differences are pretty small in general (1-2%).

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (6 files, .tar.gz)

0004-BRIN-bloom-indexes-20200913.patch (text/plain, 138.7K) download From 2c6933aebef102df53c145a6ae6c794e7860883f Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 4/6] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..4e35bb3459 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -934,6 +1075,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -984,6 +1142,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1195,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1229,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1262,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1314,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1347,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1472,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1542,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1576,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1636,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1669,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1722,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 925b262b60..dc17d4ce38 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8127,6 +8127,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10973,3 +10993,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20200913.patch (text/plain, 23.3K) download From 5106be5ed355cdaca5becf7c215df95bb8dd45ea Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:28 +0200 Subject: [PATCH 2/6] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit, and it simplifies the support procedures quite a bit, as they don't need to care about NULL values and flags at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 294 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 240 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..f438e59c75 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +542,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +735,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +763,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1543,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1629,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index a3bb3558ec..a625ef7aac 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0003-optimize-allocations-20200913.patch (text/plain, 3.2K) download From e79f8905ecb5271e82d560a0ce2e5ab4342eb2b4 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/6] optimize allocations --- src/backend/access/brin/brin.c | 52 +++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f438e59c75..756dc0fd1f 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -373,6 +373,8 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -398,11 +400,47 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -451,17 +489,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.25.4
0004-BRIN-bloom-indexes-20200913.patch (text/plain, 138.7K) download From 2c6933aebef102df53c145a6ae6c794e7860883f Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 4/6] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883f..4e35bb3459 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -770,6 +770,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -783,6 +801,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -796,6 +832,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -897,6 +951,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -910,6 +1016,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -922,6 +1046,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -934,6 +1075,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -984,6 +1142,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -998,6 +1195,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1012,6 +1229,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1025,6 +1262,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1059,6 +1314,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1072,6 +1347,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1179,6 +1472,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1193,6 +1542,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1207,6 +1576,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1247,6 +1636,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1260,6 +1669,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1295,6 +1722,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 925b262b60..dc17d4ce38 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8127,6 +8127,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10973,3 +10993,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-BRIN-minmax-multi-indexes-20200913.patch (text/plain, 217.3K) download From 26b756c0e84c745b1da8d3fe71bb583c6e68fc67 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:49 +0200 Subject: [PATCH 5/6] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2390 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4793 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..d86e3ed8f2 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..227927fcf5 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2390 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} + diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 4e35bb3459..082fbe10b5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -951,6 +951,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1046,6 +1192,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1092,6 +1255,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1142,6 +1322,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1195,6 +1449,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1229,6 +1503,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1262,6 +1556,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1347,6 +1661,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1472,6 +1805,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1542,6 +2039,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1576,6 +2093,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1636,6 +2173,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1667,7 +2224,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1722,6 +2300,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index dc17d4ce38..77735c3abe 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8110,6 +8110,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11007,3 +11072,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0006-Ignore-correlation-for-new-BRIN-opclasses-20200913.patch (text/plain, 4.1K) download From 738fdb48b797d87125a6fea58f52c539b0feaa1d Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 6/6] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index c2cbbd9400..03e9d4b713 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 227927fcf5..c8f36b0c8a 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-17 14:33                                                                                                                           ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-17 14:33 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Sun, Sep 13, 2020 at 12:40 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
> <20200913 patch set>

Hi Tomas,

The cfbot fails to apply this, but with 0001 from 0912 it works on my
end, so going with that.

One problem I have is I don't get success with the new reloptions:

create index cd_multi on iot using brin(create_dt
timestamptz_minmax_multi_ops) with (values_per_range = 64);
ERROR:  unrecognized parameter "values_per_range"

create index  on iot using brin(create_dt timestamptz_bloom_ops) with
(n_distinct_per_range = 16);
ERROR:  unrecognized parameter "n_distinct_per_range"


Aside from that, I'm going to try to understand the code, and ask
questions. Some of the code might still change, but I don't think it's
too early to do some comment and docs proofreading. I'll do this in
separate emails for bloom and multi-minmax to keep it from being too
long. Perf testing will come sometime later.


Bloom
-----

+     greater than 0.0 and smaller than 1.0. The default values is 0.01,

+     rows per block). The default values is <literal>-0.1</literal>, and

s/values/value/

+     the minimum number of distinct non-null values is <literal>128</literal>.

I don't see 128 in the code, but I do see this, is this the intention?:

#define BLOOM_MIN_NDISTINCT_PER_RANGE 16

+ * Bloom filters allow efficient test whether a given page range contains
+ * a particular value. Therefore, if we summarize each page range into a
+ * bloom filter, we can easily and cheaply test wheter it containst values
+ * we get later.

s/test/testing/
s/wheter it containst/whether it contains/

+ * The index only supports equality operator, similarly to hash indexes.

s/operator/operators/

+ * The number of distinct elements (in a page range) depends on the data,
+ * we can consider it fixed. This simplifies the trade-off to just false
+ * positive rate vs. size.

Sounds like the first sentence should start with "although".

+ * of distinct items to be stored in the filter. We can't quite the input
+ * data, of course, but we may make the BRIN page ranges smaller - instead

I think you accidentally a word.

+ * Of course, good sizing decisions depend on having the necessary data,
+ * i.e. number of distinct values in a page range (of a given size) and
+ * table size (to estimate cost change due to change in false positive
+ * rate due to having larger index vs. scanning larger indexes). We may
+ * not have that data - for example when building an index on empty table
+ * it's not really possible. And for some data we only have estimates for
+ * the whole table and we can only estimate per-range values (ndistinct).

and

+ * The current logic, implemented in brin_bloom_get_ndistinct, attempts to
+ * make some basic sizing decisions, based on the table ndistinct estimate.

+ * XXX We might also fetch info about ndistinct estimate for the column,
+ * and compute the expected number of distinct values in a range. But

Maybe I'm missing something, but the first two comments don't match
the last one -- I don't see where we get table ndistinct, which I take
to mean from the stats catalog?

+ * To address these issues, the opclass stores the raw values directly, and
+ * only switches to the actual bloom filter after reaching the same space
+ * requirements.

IIUC, it's after reaching a certain size (BLOOM_MAX_UNSORTED * 4), so
"same" doesn't make sense here.

+ /*
+ * The 1% value is mostly arbitrary, it just looks nice.
+ */
+#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */

I think we'd want better stated justification for this default, even
if just precedence in other implementations. Maybe I can test some
other values here?

+ * XXX Perhaps we could save a few bytes by using different data types, but
+ * considering the size of the bitmap, the difference is negligible.

Yeah, I think it's obvious enough to leave out.

+ m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 /
(pow(2.0, log(2.0)))));

I find this pretty hard to read and pgindent is going to mess it up
further. I would have a comment with the formula in math notation
(note that you can dispense with the reciprocal and just use
negation), but in code fold the last part to a constant. That might go
against project style, though:

m = ceil(ndistinct * log(false_positive_rate) * -2.08136);

+ * XXX Maybe the 64B min size is not really needed?

Something to figure out before commit?

+ /* assume 'not updated' by default */
+ Assert(filter);

I don't see how these are related.

+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));

I'm curious about the Int64 part -- most callers use the bare value or
with DatumGetUInt32().

Also, is there a reference for the algorithm for hash values that
follows? I didn't see anything like it in my cursory reading of the
topic. Might be good to include it in the comments.

+ * Tweak the ndistinct value based on the pagesPerRange value. First,

Nitpick: "Tweak" to my mind means to adjust an existing value. The
above is only true if ndistinct is negative, but we're really not
tweaking, but using it as a scale factor. Otherwise it's not adjusted,
only clamped.

+ * XXX We can only do this when the pagesPerRange value was supplied.
+ * If it wasn't, it has to be a read-only access to the index, in which
+ * case we don't really care. But perhaps we should fall-back to the
+ * default pagesPerRange value?

I don't understand this.

+static double
+brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts)
+{
+ return BloomGetFalsePositiveRate(opts);
+}

The body of the function is just a macro not used anywhere else -- is
there a reason for having the macro? Also, what's the first parameter
for?

Similarly, BloomGetNDistinctPerRange could just be inlined or turned
into a function for readability.

+ * or null if it is not exists.

s/is not exists/does not exist/

+ /*
+ * XXX not sure the detoasting is necessary (probably not, this
+ * can only be in an index).
+ */

Something to find out before commit?

+ /* TODO include the sorted/unsorted values */

Patch TODO or future TODO?

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-17 16:34                                                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 19:19                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-09-17 16:34 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 17, 2020 at 10:33:06AM -0400, John Naylor wrote:
>On Sun, Sep 13, 2020 at 12:40 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> <20200913 patch set>
>
>Hi Tomas,
>
>The cfbot fails to apply this, but with 0001 from 0912 it works on my
>end, so going with that.
>

Hmm, it seems to fail because of some whitespace errors. Attached is an
updated version resolving that.

>One problem I have is I don't get success with the new reloptions:
>
>create index cd_multi on iot using brin(create_dt
>timestamptz_minmax_multi_ops) with (values_per_range = 64);
>ERROR:  unrecognized parameter "values_per_range"
>
>create index  on iot using brin(create_dt timestamptz_bloom_ops) with
>(n_distinct_per_range = 16);
>ERROR:  unrecognized parameter "n_distinct_per_range"
>

But those are opclass parameters, so the parameters are not specified in
WITH clause, but right after the opclass name:

CREATE INDEX idx ON table USING brin (
	bigint_col int8_minmax_multi_ops(values_per_range = 15)
);


>
>Aside from that, I'm going to try to understand the code, and ask
>questions. Some of the code might still change, but I don't think it's
>too early to do some comment and docs proofreading. I'll do this in
>separate emails for bloom and multi-minmax to keep it from being too
>long. Perf testing will come sometime later.
>

OK.

>
>Bloom
>-----
>
>+     greater than 0.0 and smaller than 1.0. The default values is 0.01,
>
>+     rows per block). The default values is <literal>-0.1</literal>, and
>
>s/values/value/
>
>+     the minimum number of distinct non-null values is <literal>128</literal>.
>
>I don't see 128 in the code, but I do see this, is this the intention?:
>
>#define BLOOM_MIN_NDISTINCT_PER_RANGE 16
>

Ah, that's right - I might have lowered the default after writing the
comment. Will fix.

>+ * Bloom filters allow efficient test whether a given page range contains
>+ * a particular value. Therefore, if we summarize each page range into a
>+ * bloom filter, we can easily and cheaply test wheter it containst values
>+ * we get later.
>
>s/test/testing/
>s/wheter it containst/whether it contains/
>

OK, will reword.

>+ * The index only supports equality operator, similarly to hash indexes.
>
>s/operator/operators/
>

Hmmm, are there really multiple equality operators?

>+ * The number of distinct elements (in a page range) depends on the data,
>+ * we can consider it fixed. This simplifies the trade-off to just false
>+ * positive rate vs. size.
>
>Sounds like the first sentence should start with "although".
>

Yeah, probably. Or maybe there should be "but" at the beginning of the
second sentence.

>+ * of distinct items to be stored in the filter. We can't quite the input
>+ * data, of course, but we may make the BRIN page ranges smaller - instead
>
>I think you accidentally a word.
>

Seems like that.

>+ * Of course, good sizing decisions depend on having the necessary data,
>+ * i.e. number of distinct values in a page range (of a given size) and
>+ * table size (to estimate cost change due to change in false positive
>+ * rate due to having larger index vs. scanning larger indexes). We may
>+ * not have that data - for example when building an index on empty table
>+ * it's not really possible. And for some data we only have estimates for
>+ * the whole table and we can only estimate per-range values (ndistinct).
>
>and
>
>+ * The current logic, implemented in brin_bloom_get_ndistinct, attempts to
>+ * make some basic sizing decisions, based on the table ndistinct estimate.
>
>+ * XXX We might also fetch info about ndistinct estimate for the column,
>+ * and compute the expected number of distinct values in a range. But
>
>Maybe I'm missing something, but the first two comments don't match
>the last one -- I don't see where we get table ndistinct, which I take
>to mean from the stats catalog?
>

Ah, right. The part suggesting we're looking at the table n_distinct
estimate is obsolete - some older version of the patch attempted to do
that, but I decided to remove it at some point. We can add it in the
future, but I'll fix the comment for now.

>+ * To address these issues, the opclass stores the raw values directly, and
>+ * only switches to the actual bloom filter after reaching the same space
>+ * requirements.
>
>IIUC, it's after reaching a certain size (BLOOM_MAX_UNSORTED * 4), so
>"same" doesn't make sense here.
>

Ummm, no. BLOOM_MAX_UNSORTED has nothing to do with the switch from
sorted mode to hashing (which is storing an actual bloom filter).

BLOOM_MAX_UNSORTED only determines number of new items that may not
be sorted - we don't sort after each insertion, but only once in a while
to amortize the costs. So for example you may have 1000 sorted values
and then we allow adding 32 new ones before sorting the array again
(using a merge sort). But all of this is in the "sorted" mode.

The number of items the comment refers to is this:

     /* how many uint32 hashes can we fit into the bitmap */
     int maxvalues = filter->nbits / (8 * sizeof(uint32));

where nbits is the size of the bloom filter. So I think the "same" is
quite right here.

>+ /*
>+ * The 1% value is mostly arbitrary, it just looks nice.
>+ */
>+#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */
>
>I think we'd want better stated justification for this default, even
>if just precedence in other implementations. Maybe I can test some
>other values here?
>

Well, I don't know how to pick a better default :-( Ultimately it's a
tarde-off between larger indexes and scanning larger fraction of a table
due to lower false positive. Then there's the restriction that the whole
index tuple needs to fit into a single 8kB page.

>+ * XXX Perhaps we could save a few bytes by using different data types, but
>+ * considering the size of the bitmap, the difference is negligible.
>
>Yeah, I think it's obvious enough to leave out.
>
>+ m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 /
>(pow(2.0, log(2.0)))));
>
>I find this pretty hard to read and pgindent is going to mess it up
>further. I would have a comment with the formula in math notation
>(note that you can dispense with the reciprocal and just use
>negation), but in code fold the last part to a constant. That might go
>against project style, though:
>
>m = ceil(ndistinct * log(false_positive_rate) * -2.08136);
>

Hmm, maybe. I've mostly just copied this out from some bloom filter
paper, but maybe it's not readable.

>+ * XXX Maybe the 64B min size is not really needed?
>
>Something to figure out before commit?
>

Probably. I think this optimization is somewhat pointless and we should
just allocate the right amount of space, and repalloc if needed.

>+ /* assume 'not updated' by default */
>+ Assert(filter);
>

I think they are not related, although the formatting might make it seem
like that.

>I don't see how these are related.
>
>+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));
>
>I'm curious about the Int64 part -- most callers use the bare value or
>with DatumGetUInt32().
>

Yeah, that formula should use DatumGetUInt32.

>Also, is there a reference for the algorithm for hash values that
>follows? I didn't see anything like it in my cursory reading of the
>topic. Might be good to include it in the comments.
>

This was suggested by Yura Sokolov [1] in 2017. The post refers to a
paper [2] but I don't recall which part describes "our" algorithm.

[1] https://www.postgresql.org/message-id/94c173b54a0aef6ae9b18157ef52f03e@postgrespro.ru
[2] https://www.eecs.harvard.edu/~michaelm/postscripts/rsa2008.pdf


>+ * Tweak the ndistinct value based on the pagesPerRange value. First,
>
>Nitpick: "Tweak" to my mind means to adjust an existing value. The
>above is only true if ndistinct is negative, but we're really not
>tweaking, but using it as a scale factor. Otherwise it's not adjusted,
>only clamped.
>

OK. Perhaps 'adjust' would be a better term?

>+ * XXX We can only do this when the pagesPerRange value was supplied.
>+ * If it wasn't, it has to be a read-only access to the index, in which
>+ * case we don't really care. But perhaps we should fall-back to the
>+ * default pagesPerRange value?
>
>I don't understand this.
>

IIRC I thought there were situations when pagesPerRange value is not
defined, e.g. in read-only access. But I'm not sure about this, and
cosidering the code actally does not check for that (in fact, it has an
assert enforcing valid block number) I think it's a stale comment.


>+static double
>+brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts)
>+{
>+ return BloomGetFalsePositiveRate(opts);
>+}
>
>The body of the function is just a macro not used anywhere else -- is
>there a reason for having the macro? Also, what's the first parameter
>for?
>

No reason. I think the function used to be more complicated at some
point, but it got simpler.

>Similarly, BloomGetNDistinctPerRange could just be inlined or turned
>into a function for readability.
>

Same story.

>+ * or null if it is not exists.
>
>s/is not exists/does not exist/
>
>+ /*
>+ * XXX not sure the detoasting is necessary (probably not, this
>+ * can only be in an index).
>+ */
>
>Something to find out before commit?
>
>+ /* TODO include the sorted/unsorted values */
>

This was simplemented as part of the discussion about pageinspect, and
I wanted some confirmation if the approach is acceptable or not before
spending more time on it. Also, the values are really just hashes of the
column values, so I'm not quite sure it makes sense to include that.
What would you suggest?


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (6 files, .tar.gz)

0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20200917.patch (text/plain, 23.5K) download From 83608b94f83080c5c46eeda684ed14a2baee2005 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/6] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..b233558310 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 96d7efd427..2d013d4221 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8107,7 +8107,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8123,7 +8123,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20200917.patch (text/plain, 23.3K) download From 3c271506ab49e04d9eb3528aea21c72efcb7f730 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 17 Sep 2020 17:26:10 +0200 Subject: [PATCH 2/6] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit, and it simplifies the support procedures quite a bit, as they don't need to care about NULL values and flags at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 294 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 240 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..f438e59c75 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +542,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +735,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +763,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1543,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1629,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index b233558310..8886ad984e 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0003-optimize-allocations-20200917.patch (text/plain, 3.2K) download From 506f74d961b1bb3e413f8cfce05d5ef183202866 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/6] optimize allocations --- src/backend/access/brin/brin.c | 52 +++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f438e59c75..756dc0fd1f 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -373,6 +373,8 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -398,11 +400,47 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -451,17 +489,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.25.4
0004-BRIN-bloom-indexes-20200917.patch (text/plain, 138.7K) download From a7fd79ab6eb40ffd982efa063edc2738bc6d9d5f Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 4/6] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index a8e0c4ff8a..15f3baea15 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -772,6 +772,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -785,6 +803,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -798,6 +834,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -899,6 +953,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -912,6 +1018,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -924,6 +1048,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -936,6 +1077,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -986,6 +1144,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1000,6 +1197,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1014,6 +1231,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1027,6 +1264,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1061,6 +1316,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1074,6 +1349,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1181,6 +1474,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1195,6 +1544,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1209,6 +1578,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1249,6 +1638,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1262,6 +1671,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1297,6 +1724,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 2d013d4221..321ab372f4 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8130,6 +8130,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10976,3 +10996,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-BRIN-minmax-multi-indexes-20200917.patch (text/plain, 217.3K) download From 95d9628fcef75323294e3f6f8ad3d3e52f3c764a Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:49 +0200 Subject: [PATCH 5/6] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2389 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4792 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..d86e3ed8f2 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..62643e49d4 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2389 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 15f3baea15..8d212ede8b 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -953,6 +953,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1048,6 +1194,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1094,6 +1257,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1144,6 +1324,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1197,6 +1451,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1231,6 +1505,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1264,6 +1558,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1349,6 +1663,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1474,6 +1807,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1544,6 +2041,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1578,6 +2095,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1638,6 +2175,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1669,7 +2226,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1724,6 +2302,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 321ab372f4..206edf0963 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8113,6 +8113,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11010,3 +11075,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0006-Ignore-correlation-for-new-BRIN-opclasses-20200917.patch (text/plain, 4.1K) download From b997e7746ffdad3fc23f9941655b38c15b76c6d7 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 6/6] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index c2cbbd9400..03e9d4b713 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 62643e49d4..6a90b1610f 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-17 19:19                                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-09-17 19:19 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

OK,

cfbot was not quite happy with the last version either - there was a bug
in 0003 part, allocating smaller chunk of memory than needed. Attached
is a version fixing that, hopefully cfbot will be happy with this one.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments: download all (6 files, .tar.gz)

0001-Pass-all-scan-keys-to-BRIN-consistent-func-20200917b.patch (text/plain, 23.5K) download From 83608b94f83080c5c46eeda684ed14a2baee2005 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/6] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..b233558310 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 96d7efd427..2d013d4221 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8107,7 +8107,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8123,7 +8123,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.25.4
0002-Move-IS-NOT-NULL-handling-from-BRIN-suppor-20200917b.patch (text/plain, 23.3K) download From 3c271506ab49e04d9eb3528aea21c72efcb7f730 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 17 Sep 2020 17:26:10 +0200 Subject: [PATCH 2/6] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit, and it simplifies the support procedures quite a bit, as they don't need to care about NULL values and flags at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 294 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 240 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..f438e59c75 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +542,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +735,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +763,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1543,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1629,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index b233558310..8886ad984e 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
0003-optimize-allocations-20200917b.patch (text/plain, 3.9K) download From aaa523a489ec02e1b2ac00f7cfd866859e4e4b94 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/6] optimize allocations --- src/backend/access/brin/brin.c | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f438e59c75..bd052c8b0b 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -373,6 +373,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; + char *tmp PG_USED_FOR_ASSERTS_ONLY; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -398,11 +401,50 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + tmp = ptr; + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + Assert(tmp + len == ptr); + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -438,9 +480,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) { FmgrInfo *tmp; - /* No key/null arrays for this attribute. */ - Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); - Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); + /* First time we see this attribute, so no key/null keys. */ + Assert(nkeys[keyattno - 1] == 0); + Assert(nnullkeys[keyattno - 1] == 0); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -451,17 +493,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.25.4
0004-BRIN-bloom-indexes-20200917b.patch (text/plain, 138.7K) download From bcb684685028d00173fed5c703afdecc79f7f9a2 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 4/6] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 33aa64e81d..9c90d451a7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..c2cbbd9400 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index a8e0c4ff8a..15f3baea15 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -772,6 +772,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -785,6 +803,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -798,6 +834,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -899,6 +953,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -912,6 +1018,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -924,6 +1048,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -936,6 +1077,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -986,6 +1144,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1000,6 +1197,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1014,6 +1231,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1027,6 +1264,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1061,6 +1316,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1074,6 +1349,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1181,6 +1474,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1195,6 +1544,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1209,6 +1578,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1249,6 +1638,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1262,6 +1671,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1297,6 +1724,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 2d013d4221..321ab372f4 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8130,6 +8130,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10976,3 +10996,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index b2cec07416..a41c2e5418 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -631,3 +631,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..dca8e9eb34 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..b49239b1d0 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..abce8e180a 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -107,6 +107,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.25.4
0005-BRIN-minmax-multi-indexes-20200917b.patch (text/plain, 217.3K) download From 84dc19e447d353f6fb52ae8eeb5fc694740f1e81 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:49 +0200 Subject: [PATCH 5/6] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2389 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4792 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 9c90d451a7..d86e3ed8f2 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..62643e49d4 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2389 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 15f3baea15..8d212ede8b 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -953,6 +953,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1048,6 +1194,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1094,6 +1257,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1144,6 +1324,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1197,6 +1451,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1231,6 +1505,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1264,6 +1558,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1349,6 +1663,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1474,6 +1807,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1544,6 +2041,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1578,6 +2095,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1638,6 +2175,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1669,7 +2226,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1724,6 +2302,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 321ab372f4..206edf0963 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8113,6 +8113,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11010,3 +11075,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a41c2e5418..c189b35a3d 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -638,3 +638,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b49239b1d0..a933db5456 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index abce8e180a..3f05a7061f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.25.4
0006-Ignore-correlation-for-new-BRIN-opclasses-20200917b.patch (text/plain, 4.1K) download From 00a87aa8eff3c2ab046f3319470ebfbe38f56fc1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 6/6] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index c2cbbd9400..03e9d4b713 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index 62643e49d4..6a90b1610f 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..c00265a66d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.25.4
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-17 21:42                                                                                                                               ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:38                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 2 replies; 177+ messages in thread

From: John Naylor @ 2020-09-17 21:42 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 17, 2020 at 12:34 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
>
> On Thu, Sep 17, 2020 at 10:33:06AM -0400, John Naylor wrote:
> >On Sun, Sep 13, 2020 at 12:40 PM Tomas Vondra
> ><tomas.vondra@2ndquadrant.com> wrote:
> >> <20200913 patch set>
> But those are opclass parameters, so the parameters are not specified in
> WITH clause, but right after the opclass name:
>
> CREATE INDEX idx ON table USING brin (
>         bigint_col int8_minmax_multi_ops(values_per_range = 15)
> );

D'oh!

> >+ * The index only supports equality operator, similarly to hash indexes.
> >
> >s/operator/operators/
> >
>
> Hmmm, are there really multiple equality operators?

Ah, I see what you meant -- then "_the_ equality operator" is what we want.

> The number of items the comment refers to is this:
>
>      /* how many uint32 hashes can we fit into the bitmap */
>      int maxvalues = filter->nbits / (8 * sizeof(uint32));
>
> where nbits is the size of the bloom filter. So I think the "same" is
> quite right here.

Ok, I get it now.

> >+ /*
> >+ * The 1% value is mostly arbitrary, it just looks nice.
> >+ */
> >+#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */
> >
> >I think we'd want better stated justification for this default, even
> >if just precedence in other implementations. Maybe I can test some
> >other values here?
> >
>
> Well, I don't know how to pick a better default :-( Ultimately it's a
> tarde-off between larger indexes and scanning larger fraction of a table
> due to lower false positive. Then there's the restriction that the whole
> index tuple needs to fit into a single 8kB page.

Well, it might be a perfectly good default, and it seems common in
articles on the topic, but the comment is referring to aesthetics. :-)
I still intend to test some cases.

> >Also, is there a reference for the algorithm for hash values that
> >follows? I didn't see anything like it in my cursory reading of the
> >topic. Might be good to include it in the comments.
> >
>
> This was suggested by Yura Sokolov [1] in 2017. The post refers to a
> paper [2] but I don't recall which part describes "our" algorithm.
>
> [1] https://www.postgresql.org/message-id/94c173b54a0aef6ae9b18157ef52f03e@postgrespro.ru
> [2] https://www.eecs.harvard.edu/~michaelm/postscripts/rsa2008.pdf

Hmm, I came across that paper while doing background reading. Okay,
now I get that "% (filter->nbits - 1)" is the second hash function in
that scheme. But now I wonder if that second function should actually
act on the passed "value" (the original hash), so that they are
actually independent, as required. In the language of that paper, the
patch seems to have

g(x) = h1(x) + i*h2(h1(x)) + f(i)

instead of

g(x) = h1(x) + i*h2(x) + f(i)

Concretely, I'm wondering if it should be:

 big_h = DatumGetUint32(hash_uint32(value));
 h = big_h % filter->nbits;
-d = big_h % (filter->nbits - 1);
+d = value % (filter->nbits - 1);

But I could be wrong.

Also, I take it that f(i) = 1 in the patch, hence the increment here:

+ h += d++;

But it's a little hidden. That might be worth commenting, if I haven't
completely missed something.

> >+ * Tweak the ndistinct value based on the pagesPerRange value. First,
> >
> >Nitpick: "Tweak" to my mind means to adjust an existing value. The
> >above is only true if ndistinct is negative, but we're really not
> >tweaking, but using it as a scale factor. Otherwise it's not adjusted,
> >only clamped.
> >
>
> OK. Perhaps 'adjust' would be a better term?

I felt like rewriting the whole thing, but your original gets the
point across ok, really.

"If the passed ndistinct value is positive, we can just use that, but
we also clamp the value to prevent over-sizing the bloom filter
unnecessarily. If it's negative, it represents a multiplier to apply
to the maximum number of tuples in the range (assuming each page gets
MaxHeapTuplesPerPage tuples, which is likely a significant
over-estimate), similar to the corresponding value in table
statistics."

> >+ /* TODO include the sorted/unsorted values */
> >
>
> This was simplemented as part of the discussion about pageinspect, and
> I wanted some confirmation if the approach is acceptable or not before
> spending more time on it. Also, the values are really just hashes of the
> column values, so I'm not quite sure it makes sense to include that.
> What would you suggest?

My gut feeling is the hashes are not useful for this purpose, but I
don't feel strongly either way.

--
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-17 22:48                                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  1 sibling, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-17 22:48 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

I wrote:

> Hmm, I came across that paper while doing background reading. Okay,
> now I get that "% (filter->nbits - 1)" is the second hash function in
> that scheme. But now I wonder if that second function should actually
> act on the passed "value" (the original hash), so that they are
> actually independent, as required. In the language of that paper, the
> patch seems to have
>
> g(x) = h1(x) + i*h2(h1(x)) + f(i)
>
> instead of
>
> g(x) = h1(x) + i*h2(x) + f(i)
>
> Concretely, I'm wondering if it should be:
>
>  big_h = DatumGetUint32(hash_uint32(value));
>  h = big_h % filter->nbits;
> -d = big_h % (filter->nbits - 1);
> +d = value % (filter->nbits - 1);
>
> But I could be wrong.

I'm wrong -- if we use different operands to the moduli, we throw away
the assumption of co-primeness. But I'm still left wondering why we
have to re-hash the hash for this to work. In any case, there should
be some more documentation around the core algorithm, so that future
readers are not left scratching their heads.

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-18 11:40                                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-18 11:40 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 17, 2020 at 06:48:11PM -0400, John Naylor wrote:
>I wrote:
>
>> Hmm, I came across that paper while doing background reading. Okay,
>> now I get that "% (filter->nbits - 1)" is the second hash function in
>> that scheme. But now I wonder if that second function should actually
>> act on the passed "value" (the original hash), so that they are
>> actually independent, as required. In the language of that paper, the
>> patch seems to have
>>
>> g(x) = h1(x) + i*h2(h1(x)) + f(i)
>>
>> instead of
>>
>> g(x) = h1(x) + i*h2(x) + f(i)
>>
>> Concretely, I'm wondering if it should be:
>>
>>  big_h = DatumGetUint32(hash_uint32(value));
>>  h = big_h % filter->nbits;
>> -d = big_h % (filter->nbits - 1);
>> +d = value % (filter->nbits - 1);
>>
>> But I could be wrong.
>
>I'm wrong -- if we use different operands to the moduli, we throw away
>the assumption of co-primeness. But I'm still left wondering why we
>have to re-hash the hash for this to work. In any case, there should
>be some more documentation around the core algorithm, so that future
>readers are not left scratching their heads.
>

Hmm, good question. I think we don't really need to hash it twice. It
does not rally achieve anything - it won't reduce number of collisions
or anything like that.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-18 21:06                                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-18 21:06 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 18, 2020 at 7:40 AM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
>
> On Thu, Sep 17, 2020 at 06:48:11PM -0400, John Naylor wrote:
> >I wrote:
> >
> >> Hmm, I came across that paper while doing background reading. Okay,
> >> now I get that "% (filter->nbits - 1)" is the second hash function in
> >> that scheme. But now I wonder if that second function should actually
> >> act on the passed "value" (the original hash), so that they are
> >> actually independent, as required. In the language of that paper, the
> >> patch seems to have
> >>
> >> g(x) = h1(x) + i*h2(h1(x)) + f(i)
> >>
> >> instead of
> >>
> >> g(x) = h1(x) + i*h2(x) + f(i)
> >>
> >> Concretely, I'm wondering if it should be:
> >>
> >>  big_h = DatumGetUint32(hash_uint32(value));
> >>  h = big_h % filter->nbits;
> >> -d = big_h % (filter->nbits - 1);
> >> +d = value % (filter->nbits - 1);
> >>
> >> But I could be wrong.
> >
> >I'm wrong -- if we use different operands to the moduli, we throw away
> >the assumption of co-primeness. But I'm still left wondering why we
> >have to re-hash the hash for this to work. In any case, there should
> >be some more documentation around the core algorithm, so that future
> >readers are not left scratching their heads.
> >
>
> Hmm, good question. I think we don't really need to hash it twice. It
> does not rally achieve anything - it won't reduce number of collisions
> or anything like that.

Yeah, looking back at the discussion you linked previously, I think
it's a holdover from when the uint32 was rehashed with k different
seeds. Anyway, after thinking about it some more, I still have doubts
about the mapping algorithm. There are two stages to a hash mapping --
hashing and modulus. I don't think a single hash function (whether
rehashed or not) can be turned into two independent functions via a
choice of second modulus. At least, that's not what the Kirsch &
Mitzenmacher paper is claiming. Since we're not actually applying two
independent hash functions on the scan key, we're kind of shooting in
the dark.

It turns out there is something called a one-hash bloom filter, and
the paper in [1] has a straightforward algorithm. Since we can
implement it exactly as stated in the paper, that gives me more
confidence in the real-world false positive rate. It goes like this:

Partition the filter bitmap into k partitions of similar but unequal
length, corresponding to consecutive prime numbers. Use the primes for
moduli of the uint32 value and map it to the bit of the corresponding
partition. For a simple example, let's use 7, 11, 13 for partitions in
a filter of size 31. The three bits are:

value % 7
7 + (value % 11)
7 + 11 + (value % 13)

We could store a const array of the first 256 primes. The largest such
prime is 1613, so with k=7 we can support up to ~11k bits, which is
more than we'd like to store anyway. Then we store the array index of
the largest prime in the 8bits of padding we currently have in
BloomFilter struct.

One wrinkle is that the sum of k primes is not going to match m
exactly. If the sum is too small, we can trim a few bits off of the
filter bitmap. If the sum is too large, the last partition can spill
into the front of the first one. This shouldn't matter much in the
common case since we need to round m to the nearest byte anyway.

This should be pretty straightforward to turn into code and I can take
a stab at it. Thoughts?

[1] https://www.researchgate.net/publication/284283336_One-Hashing_Bloom_Filter

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-18 22:27                                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-18 22:27 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 18, 2020 at 05:06:49PM -0400, John Naylor wrote:
>On Fri, Sep 18, 2020 at 7:40 AM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>>
>> On Thu, Sep 17, 2020 at 06:48:11PM -0400, John Naylor wrote:
>> >I wrote:
>> >
>> >> Hmm, I came across that paper while doing background reading. Okay,
>> >> now I get that "% (filter->nbits - 1)" is the second hash function in
>> >> that scheme. But now I wonder if that second function should actually
>> >> act on the passed "value" (the original hash), so that they are
>> >> actually independent, as required. In the language of that paper, the
>> >> patch seems to have
>> >>
>> >> g(x) = h1(x) + i*h2(h1(x)) + f(i)
>> >>
>> >> instead of
>> >>
>> >> g(x) = h1(x) + i*h2(x) + f(i)
>> >>
>> >> Concretely, I'm wondering if it should be:
>> >>
>> >>  big_h = DatumGetUint32(hash_uint32(value));
>> >>  h = big_h % filter->nbits;
>> >> -d = big_h % (filter->nbits - 1);
>> >> +d = value % (filter->nbits - 1);
>> >>
>> >> But I could be wrong.
>> >
>> >I'm wrong -- if we use different operands to the moduli, we throw away
>> >the assumption of co-primeness. But I'm still left wondering why we
>> >have to re-hash the hash for this to work. In any case, there should
>> >be some more documentation around the core algorithm, so that future
>> >readers are not left scratching their heads.
>> >
>>
>> Hmm, good question. I think we don't really need to hash it twice. It
>> does not rally achieve anything - it won't reduce number of collisions
>> or anything like that.
>
>Yeah, looking back at the discussion you linked previously, I think
>it's a holdover from when the uint32 was rehashed with k different
>seeds. Anyway, after thinking about it some more, I still have doubts
>about the mapping algorithm. There are two stages to a hash mapping --
>hashing and modulus. I don't think a single hash function (whether
>rehashed or not) can be turned into two independent functions via a
>choice of second modulus. At least, that's not what the Kirsch &
>Mitzenmacher paper is claiming. Since we're not actually applying two
>independent hash functions on the scan key, we're kind of shooting in
>the dark.
>

OK. I admit the modulo by nbits and (nbits - 1) is a bit suspicious, so
you may be right this is not quite correct construction.

The current scheme was meant to reduce the number of expensive hashing
calls (especially for low fpr values we may require quite a few of
those, easily 10 or more.

But maybe we could still use this scheme by actually computing

    h1 = hash_uint32_extended(value, seed1);
    h2 = hash_uint32_extended(value, seed2);

and then use this as the independent hash functions. I think that would
meet the requirements of the paper.

>It turns out there is something called a one-hash bloom filter, and
>the paper in [1] has a straightforward algorithm. Since we can
>implement it exactly as stated in the paper, that gives me more
>confidence in the real-world false positive rate. It goes like this:
>
>Partition the filter bitmap into k partitions of similar but unequal
>length, corresponding to consecutive prime numbers. Use the primes for
>moduli of the uint32 value and map it to the bit of the corresponding
>partition. For a simple example, let's use 7, 11, 13 for partitions in
>a filter of size 31. The three bits are:
>
>value % 7
>7 + (value % 11)
>7 + 11 + (value % 13)
>
>We could store a const array of the first 256 primes. The largest such
>prime is 1613, so with k=7 we can support up to ~11k bits, which is
>more than we'd like to store anyway. Then we store the array index of
>the largest prime in the 8bits of padding we currently have in
>BloomFilter struct.
>

Why would 11k bits be more than we'd like to store? Assuming we could
use the whole 8kB page for the bloom filter, that'd be about 64k bits.
In practice there'd be a bit of overhead (page header ...) but it's
still much more than 11k bits. But I guess we can simply make the table
of primes a bit larger, right?

FWIW I don't think we need to be that careful about the space to store
stuff in padding etc. If we can - great, but compared to the size of the
filter it's negligible and I'd prioritize simplicity over a byte or two.

>One wrinkle is that the sum of k primes is not going to match m
>exactly. If the sum is too small, we can trim a few bits off of the
>filter bitmap. If the sum is too large, the last partition can spill
>into the front of the first one. This shouldn't matter much in the
>common case since we need to round m to the nearest byte anyway.
>

AFAIK the paper simply says that as long as the sum of partitions is
close to the requested nbits, it's good enough. So I guess we could just
roll with that, no need to trim/wrap or something like that.

>This should be pretty straightforward to turn into code and I can take
>a stab at it. Thoughts?
>

Sure, go ahead. I'm happy someone is actually looking at those patches
and proposing alternative solutions, and this might easily be a better
hashing scheme.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-21 17:42                                                                                                                                         ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-21 17:42 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Fri, Sep 18, 2020 at 6:27 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:

> But maybe we could still use this scheme by actually computing
>
>     h1 = hash_uint32_extended(value, seed1);
>     h2 = hash_uint32_extended(value, seed2);
>
> and then use this as the independent hash functions. I think that would
> meet the requirements of the paper.

Yeah, that would work algorithmically. It would be trivial to add to
the patch, too of course. There'd be a higher up-front cpu cost. Also,
I'm a bit cautious of rehashing hashes, and whether the two values
above are independent enough. I'm not sure either of these points
matters. My guess is the partition approach is more sound, but it has
some minor organizational challenges (see below).

> Why would 11k bits be more than we'd like to store? Assuming we could
> use the whole 8kB page for the bloom filter, that'd be about 64k bits.
> In practice there'd be a bit of overhead (page header ...) but it's
> still much more than 11k bits.

Brain fade -- I guess I thought we were avoiding being toasted, but
now I see that's not possible for BRIN storage. So, we'll want to
guard against this:

ERROR:  index row size 8160 exceeds maximum 8152 for index "bar_num_idx"

While playing around with the numbers I had an epiphany: At the
defaults, the filter already takes up ~4.3kB, over half the page.
There is no room for another tuple, so if we're only indexing one
column, we might as well take up the whole page. Here MT = max tuples
per 128 8k pages, or 37120, so default ndistinct is 3712.

n      k  m      p
MT/10  7  35580  0.01
MT/10  7  64000  0.0005
MT/10  12 64000  0.00025

Assuming ndistinct isn't way off reality, we get 20x-40x lower false
positive rate almost for free, and it'd be trivial to code! Keeping k
at 7 would be more robust, since it's equivalent to starting with n =
~6000, p = 0.006, which is still almost 2x less false positives than
you asked for. It also means nearly doubling the number of sorted
values before switching.

Going the other direction, capping nbits to 64k bits when ndistinct
gets too high, the false positive rate we can actually support starts
to drop. Here, the user requested 0.001 fpr.

n       k  p
4500    9  0.001
6000    7  0.006
7500    6  0.017
15000   3  0.129  (probably useless by now)
MT      1  0.440
64000   1  0.63   (possible with > 128 pages per range)

I imagine smaller pages_per_range settings are going to be useful for
skinny tables (note to self -- test). Maybe we could provide a way for
the user to see that their combination of pages_per_range, false
positive rate, and ndistinct is supportable, like
brin_bloom_get_supported_fpr(). Or document to check with
page_inspect. And that's not even considering multi-column indexes,
like you mentioned.

> But I guess we can simply make the table
> of primes a bit larger, right?

If we want to support all the above cases without falling down
entirely, it would have to go up to 32k to be safe (When k = 1 we
could degenerate to one modulus on the whole filter). That would be a
table of about 7kB, which we could binary search. [thinks for a
moment]...Actually, if we wanted to be clever, maybe we could
precalculate the primes needed for the 64k bit cases and stick them at
the end of the array. The usual algorithm will find them. That way, we
could keep the array around 2kB. However, for >8kB block size, we
couldn't adjust the 64k number, which might be okay, but not really
project policy.

We could also generate the primes via a sieve instead, which is really
fast (and more code). That would be more robust, but that would
require the filter to store the actual primes used, so 20 more bytes
at max k = 10. We could hard-code space for that, or to keep from
hard-coding maximum k and thus lowest possible false positive rate,
we'd need more bookkeeping.

So, with the partition approach, we'd likely have to set in stone
either max nbits, or min target false positive rate. The latter option
seems more principled, not only for the block size, but also since the
target fp rate is already fixed by the reloption, and as I've
demonstrated, we can often go above and beyond the reloption even
without changing k.

> >One wrinkle is that the sum of k primes is not going to match m
> >exactly. If the sum is too small, we can trim a few bits off of the
> >filter bitmap. If the sum is too large, the last partition can spill
> >into the front of the first one. This shouldn't matter much in the
> >common case since we need to round m to the nearest byte anyway.
> >
>
> AFAIK the paper simply says that as long as the sum of partitions is
> close to the requested nbits, it's good enough. So I guess we could just
> roll with that, no need to trim/wrap or something like that.

Hmm, I'm not sure I understand you. I can see not caring to trim
wasted bits, but we can't set/read off the end of the filter. If we
don't wrap, we could just skip reading/writing that bit. So a tiny
portion of access would be at k - 1. The paper is not clear on what to
do here, but they are explicit in minimizing the absolute value, which
could go on either side.

Also I found a bug:

+ add_local_real_reloption(relopts, "false_positive_rate",
+ "desired false-positive rate for the bloom filters",
+ BLOOM_DEFAULT_FALSE_POSITIVE_RATE,
+ 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate));

When I set fp to 1.0, the reloption code is okay with that, but then
later the assertion gets triggered.

--
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-21 19:56                                                                                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-21 19:56 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Mon, Sep 21, 2020 at 01:42:34PM -0400, John Naylor wrote:
>On Fri, Sep 18, 2020 at 6:27 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>
>> But maybe we could still use this scheme by actually computing
>>
>>     h1 = hash_uint32_extended(value, seed1);
>>     h2 = hash_uint32_extended(value, seed2);
>>
>> and then use this as the independent hash functions. I think that would
>> meet the requirements of the paper.
>
>Yeah, that would work algorithmically. It would be trivial to add to
>the patch, too of course. There'd be a higher up-front cpu cost. Also,
>I'm a bit cautious of rehashing hashes, and whether the two values
>above are independent enough. I'm not sure either of these points
>matters. My guess is the partition approach is more sound, but it has
>some minor organizational challenges (see below).
>

OK. I don't think rehashing hashes is an issue as long as the original
hash has sufficiently low collision rate (and while we know it's not
perfect we know it works well enough for hash indexes etc.). And I doubt
the cost of the extra hash of uint32 would be noticeable.

That being said the partitioning approach might be more sound and it's
definitely worth giving it a try.

>> Why would 11k bits be more than we'd like to store? Assuming we could
>> use the whole 8kB page for the bloom filter, that'd be about 64k bits.
>> In practice there'd be a bit of overhead (page header ...) but it's
>> still much more than 11k bits.
>
>Brain fade -- I guess I thought we were avoiding being toasted, but
>now I see that's not possible for BRIN storage. So, we'll want to
>guard against this:
>
>ERROR:  index row size 8160 exceeds maximum 8152 for index "bar_num_idx"
>
>While playing around with the numbers I had an epiphany: At the
>defaults, the filter already takes up ~4.3kB, over half the page.
>There is no room for another tuple, so if we're only indexing one
>column, we might as well take up the whole page.

Hmm, yeah. I may be wrong but IIRC indexes don't support external
storage but compression is still allowed. So even if those defaults are
a bit higher than needed that should make the bloom filters a bit more
compressible, and thus fit multiple BRIN tuples on a single page.

>Here MT = max tuples per 128 8k pages, or 37120, so default ndistinct
>is 3712.
>
>n      k  m      p
>MT/10  7  35580  0.01
>MT/10  7  64000  0.0005
>MT/10  12 64000  0.00025
>
>Assuming ndistinct isn't way off reality, we get 20x-40x lower false
>positive rate almost for free, and it'd be trivial to code! Keeping k
>at 7 would be more robust, since it's equivalent to starting with n =
>~6000, p = 0.006, which is still almost 2x less false positives than
>you asked for. It also means nearly doubling the number of sorted
>values before switching.
>
>Going the other direction, capping nbits to 64k bits when ndistinct
>gets too high, the false positive rate we can actually support starts
>to drop. Here, the user requested 0.001 fpr.
>
>n       k  p
>4500    9  0.001
>6000    7  0.006
>7500    6  0.017
>15000   3  0.129  (probably useless by now)
>MT      1  0.440
>64000   1  0.63 (possible with > 128 pages per range)
>
>I imagine smaller pages_per_range settings are going to be useful for
>skinny tables (note to self -- test). Maybe we could provide a way for
>the user to see that their combination of pages_per_range, false
>positive rate, and ndistinct is supportable, like
>brin_bloom_get_supported_fpr(). Or document to check with page_inspect.
>And that's not even considering multi-column indexes, like you
>mentioned.
>

I agree giving users visibility into this would be useful.

Not sure about how much we want to rely on these optimizations, though,
considering multi-column indexes kinda break this.

>> But I guess we can simply make the table of primes a bit larger,
>> right?
>
>If we want to support all the above cases without falling down
>entirely, it would have to go up to 32k to be safe (When k = 1 we could
>degenerate to one modulus on the whole filter). That would be a table
>of about 7kB, which we could binary search. [thinks for a
>moment]...Actually, if we wanted to be clever, maybe we could
>precalculate the primes needed for the 64k bit cases and stick them at
>the end of the array. The usual algorithm will find them. That way, we
>could keep the array around 2kB. However, for >8kB block size, we
>couldn't adjust the 64k number, which might be okay, but not really
>project policy.
>
>We could also generate the primes via a sieve instead, which is really
>fast (and more code). That would be more robust, but that would require
>the filter to store the actual primes used, so 20 more bytes at max k =
>10. We could hard-code space for that, or to keep from hard-coding
>maximum k and thus lowest possible false positive rate, we'd need more
>bookkeeping.
>

I don't think the efficiency of this code matters too much - it's only
used once when creating the index, so the simpler the better. Certainly
for now, while testing the partitioning approach.

>So, with the partition approach, we'd likely have to set in stone
>either max nbits, or min target false positive rate. The latter option
>seems more principled, not only for the block size, but also since the
>target fp rate is already fixed by the reloption, and as I've
>demonstrated, we can often go above and beyond the reloption even
>without changing k.
>

That seems like a rather annoying limitation, TBH.

>> >One wrinkle is that the sum of k primes is not going to match m
>> >exactly. If the sum is too small, we can trim a few bits off of the
>> >filter bitmap. If the sum is too large, the last partition can spill
>> >into the front of the first one. This shouldn't matter much in the
>> >common case since we need to round m to the nearest byte anyway.
>> >
>>
>> AFAIK the paper simply says that as long as the sum of partitions is
>> close to the requested nbits, it's good enough. So I guess we could
>> just roll with that, no need to trim/wrap or something like that.
>
>Hmm, I'm not sure I understand you. I can see not caring to trim wasted
>bits, but we can't set/read off the end of the filter. If we don't
>wrap, we could just skip reading/writing that bit. So a tiny portion of
>access would be at k - 1. The paper is not clear on what to do here,
>but they are explicit in minimizing the absolute value, which could go
>on either side.
>

What I meant is that is that the paper says this:

     Given a planned overall length mp for a Bloom filter, we usually
     cannot get k prime numbers to make their sum mf to be exactly mp. As
     long as the difference between mp and mf is small enough, it neither
     causes any trouble for the software implementation nor noticeably
     shifts the false positive ratio.

Which I think means we can pick mp, generate k primes with sum mf close
to mp, and just use that with mf bits.

>Also I found a bug:
>
>+ add_local_real_reloption(relopts, "false_positive_rate", + "desired
>false-positive rate for the bloom filters", +
>BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions,
>falsePositiveRate));
>
>When I set fp to 1.0, the reloption code is okay with that, but then
>later the assertion gets triggered.
>

Hmm, yeah. I wonder what to do about that, considering indexes with fp
1.0 are essentially useless.

regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-24 21:18                                                                                                                                             ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-24 21:18 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Mon, Sep 21, 2020 at 3:56 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
>
> On Mon, Sep 21, 2020 at 01:42:34PM -0400, John Naylor wrote:

> >While playing around with the numbers I had an epiphany: At the
> >defaults, the filter already takes up ~4.3kB, over half the page.
> >There is no room for another tuple, so if we're only indexing one
> >column, we might as well take up the whole page.
>
> Hmm, yeah. I may be wrong but IIRC indexes don't support external
> storage but compression is still allowed. So even if those defaults are
> a bit higher than needed that should make the bloom filters a bit more
> compressible, and thus fit multiple BRIN tuples on a single page.

> Not sure about how much we want to rely on these optimizations, though,
> considering multi-column indexes kinda break this.

Yeah. Okay, then it sounds like we should go in the other direction,
as the block comment at the top of brin_bloom.c implies. Indexes with
multiple bloom-indexed columns already don't fit in one 8kB page, so I
think every documented example should have a much lower
pages_per_range. Using 32 pages per range with max tuples gives n =
928. With default p, that's about 1.1 kB per brin tuple, so one brin
page can index 224 pages, much more than with the default 128.

Hmm, how ugly would it be to change the default range size depending
on the opclass?

If indexes don't support external storage, that sounds like a pain to
add. Also, with very small fpr, you can easily get into many megabytes
of filter space, which kind of defeats the purpose of brin in the
first place.

There is already this item from the brin readme:

* Different-size page ranges?
  In the current design, each "index entry" in a BRIN index covers the same
  number of pages.  There's no hard reason for this; it might make sense to
  allow the index to self-tune so that some index entries cover smaller page
  ranges, if this allows the summary values to be more compact.  This
would incur
  larger BRIN overhead for the index itself, but might allow better pruning of
  page ranges during scan.  In the limit of one index tuple per page, the index
  itself would occupy too much space, even though we would be able to skip
  reading the most heap pages, because the summary values are tight; in the
  opposite limit of a single tuple that summarizes the whole table, we wouldn't
  be able to prune anything even though the index is very small.  This can
  probably be made to work by using the range map as an index in itself.

This sounds like a lot of work, but would be robust.

Anyway, given that this is a general problem and not specific to the
prime partition algorithm, I'll leave that out of the attached patch,
named as a .txt to avoid confusing the cfbot.

> >We could also generate the primes via a sieve instead, which is really
> >fast (and more code). That would be more robust, but that would require
> >the filter to store the actual primes used, so 20 more bytes at max k =
> >10. We could hard-code space for that, or to keep from hard-coding
> >maximum k and thus lowest possible false positive rate, we'd need more
> >bookkeeping.
> >
>
> I don't think the efficiency of this code matters too much - it's only
> used once when creating the index, so the simpler the better. Certainly
> for now, while testing the partitioning approach.

To check my understanding, isn't bloom_init() called for every tuple?
Agreed on simplicity so done this way.

> >So, with the partition approach, we'd likely have to set in stone
> >either max nbits, or min target false positive rate. The latter option
> >seems more principled, not only for the block size, but also since the
> >target fp rate is already fixed by the reloption, and as I've
> >demonstrated, we can often go above and beyond the reloption even
> >without changing k.
> >
>
> That seems like a rather annoying limitation, TBH.

I don't think the latter is that bad. I've capped k at 10 for
demonstration's sake.:

(928 is from using 32 pages per range)

n    k   m      p
928   7  8895   0.01
928  10  13343  0.001  (lowest p supported in patch set)
928  13  17790  0.0001
928  10  18280  0.0001 (still works with lower k, needs higher m)
928  10  17790  0.00012 (even keeping m from row #3, capping k doesn't
degrade p much)

Also, k seems pretty robust against small changes as long as m isn't
artificially constrained and as long as p is small.

So I *think* it's okay to cap k at 10 or 12, and not bother with
adjusting m, which worsens space issues. As I found before, lowering k
raises target fpr, but seems more robust to overshooting ndistinct. In
any case, we only need k * 2 bytes to store the partition lengths.

The only way I see to avoid any limitation is to make the array of
primes variable length, which could be done by putting the filter
offset calculation into a macro. But having two variable-length arrays
seems messy.

> >Hmm, I'm not sure I understand you. I can see not caring to trim wasted
> >bits, but we can't set/read off the end of the filter. If we don't
> >wrap, we could just skip reading/writing that bit. So a tiny portion of
> >access would be at k - 1. The paper is not clear on what to do here,
> >but they are explicit in minimizing the absolute value, which could go
> >on either side.
> >
>
> What I meant is that is that the paper says this:
>
>      Given a planned overall length mp for a Bloom filter, we usually
>      cannot get k prime numbers to make their sum mf to be exactly mp. As
>      long as the difference between mp and mf is small enough, it neither
>      causes any trouble for the software implementation nor noticeably
>      shifts the false positive ratio.
>
> Which I think means we can pick mp, generate k primes with sum mf close
> to mp, and just use that with mf bits.

Oh, I see. When I said "trim" I meant exactly that (when mf < mp).
Yeah, we can bump it up as well for the other case. I've done it that
way.

> >+ add_local_real_reloption(relopts, "false_positive_rate", + "desired
> >false-positive rate for the bloom filters", +
> >BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions,
> >falsePositiveRate));
> >
> >When I set fp to 1.0, the reloption code is okay with that, but then
> >later the assertion gets triggered.
> >
>
> Hmm, yeah. I wonder what to do about that, considering indexes with fp
> 1.0 are essentially useless.

Not just useless -- they're degenerate. When p = 1.0, m = k = 0 -- We
cannot accept this value from the user. Looking up thread, 0.1 was
suggested as a limit. That might be a good starting point.

This is interesting work! Having gone this far, I'm going to put more
attention to the multi-minmax patch and actually start performance
testing.

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

one-hash-bloom-filter-20200917b.txt (text/plain, 8.1K) download diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 03e9d4b713..5dc213727b 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -203,6 +203,9 @@ typedef struct BloomOptions */ #define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ +/* With the minimum allowed false positive rate of 0.001, we need up to 10 hashes */ +#define BLOOM_MAX_NUM_PARTITIONS 10 + #define BloomGetNDistinctPerRange(opts) \ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ (((BloomOptions *) (opts))->nDistinctPerRange) : \ @@ -270,6 +273,7 @@ typedef struct BloomFilter uint8 nhashes; /* number of hash functions */ uint32 nbits; /* number of bits in the bitmap (size) */ uint32 nbits_set; /* number of bits set to 1 */ + uint16 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ /* data of the bloom filter (used both for sorted and hashed phase) */ char data[FLEXIBLE_ARRAY_MEMBER]; @@ -279,6 +283,114 @@ typedef struct BloomFilter static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); +/* + * generate_primes + * returns array of all primes less than limit + * + * WIP: very naive prime sieve; could be optimized using segmented ranges + */ +static uint16 * +generate_primes(int limit) +{ + /* upper bound of number of primes below limit */ + /* WIP: reference for this number */ + int numprimes = 1.26 * limit / log(limit); + + bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); + uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + + int maxfactor = floor(sqrt(limit)); + int factor = 2; /* first prime */ + + /* mark the sieve where the index is composite */ + while (factor < maxfactor) + { + for (int i = factor * factor; i < limit; i += factor) + is_composite[i] = true; + do { factor++; } while (is_composite[factor]); + } + + /* the unmarked numbers are prime, so copy over */ + for (int i = 2, j = 0; i < limit && j < numprimes; i++) + { + if (!is_composite[i]) + primes[j++] = i; + } + + /* there should still be some zeroes at the end, but make sure */ + primes[numprimes - 1] = 0; + + /* pretty large, so free it now (segmented ranges would make it smaller) */ + pfree(is_composite); + return primes; +} + +/* + * set_bloom_partitions + * Calculate k moduli for one-hashing bloom filter. + * + * Find consecutive primes whose sum is close to nbits and + * return the sum. Copy the primes to the filter to use as + * partition lengths. + * WIP: one-hashing bf paper ref somewhere + */ +static uint32 +set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) +{ + int min, diff, incr; + int pidx = 0; + int sum = 0; + int target_partlen = nbits / nhashes; + + /* + * Increase the limit to ensure we have some primes higher than + * the target partition length. The 100 value is arbitrary, but + * should be well over what we need. + */ + uint16 *primes = generate_primes(target_partlen + 100); + + /* + * In our array of primes, find a sequence of length nhashes, whose + * last item is close to our target partition length. The end of the + * array will be filled with zeros, so we need to guard against that. + */ + while (primes[pidx + nhashes - 1] <= target_partlen && + primes[pidx] > 0) + pidx++; + + for (int i = 0; i < nhashes; i++) + sum += primes[pidx + i]; + + /* + * Since all the primes are less than or equal the desired partition + * length, the sum is somewhat less than nbits. Increment the starting + * point until we find the sequence of primes whose sum is closest to + * nbits. It doesn't matter whether it's higher or lower. + */ + min = abs(nbits - sum); + for (;;) + { + incr = primes[pidx + nhashes] - primes[pidx]; + diff = abs(nbits - (sum + incr)); + if (diff >= min) + break; + + min = diff; + sum += incr; + pidx++; + } + + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); + + /* WIP: assuming it's not important to pfree primes */ + + /* + * The actual filter length will be the sum of the partition lengths + * rounded up to the nearest byte. + */ + return (uint32) ((sum + 7) / 8) * 8; +} + /* * bloom_init * Initialize the Bloom Filter, allocate all the memory. @@ -301,15 +413,13 @@ bloom_init(int ndistinct, double false_positive_rate) m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); - /* round m to whole bytes */ - m = ((m + 7) / 8) * 8; - /* * round(log(2.0) * m / ndistinct), but assume round() may not be * available on Windows */ k = log(2.0) * m / ndistinct; k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + k = Min(k, BLOOM_MAX_NUM_PARTITIONS); /* * Allocate the bloom filter with a minimum size 64B (about 40B in the @@ -323,7 +433,9 @@ bloom_init(int ndistinct, double false_positive_rate) filter->flags = 0; /* implies SORTED phase */ filter->nhashes = (int) k; - filter->nbits = m; + + /* calculate the partition lengths and adjust m to match */ + filter->nbits = set_bloom_partitions(m, k, filter->partlens); SET_VARSIZE(filter, len); @@ -445,7 +557,7 @@ static BloomFilter * bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) { int i; - uint32 big_h, h, d; + int part_boundary = 0; /* assume 'not updated' by default */ Assert(filter); @@ -514,17 +626,16 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) /* we better be in the hashing phase */ Assert(BLOOM_IS_HASHED(filter)); - /* compute the hashes, used for the bloom filter */ - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + Assert(bitloc < filter->nbits); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); /* if the bit is not set, set it and remember we did that */ if (! (filter->data[byte] & (0x01 << bit))) @@ -536,12 +647,7 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) } /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } return filter; @@ -571,6 +677,7 @@ bloom_switch_to_hashing(BloomFilter *filter) newfilter->nhashes = filter->nhashes; newfilter->nbits = filter->nbits; + memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint16)); newfilter->flags |= BLOOM_FLAG_PHASE_HASH; SET_VARSIZE(newfilter, len); @@ -595,7 +702,7 @@ static bool bloom_contains_value(BloomFilter *filter, uint32 value) { int i; - uint32 big_h, h, d; + int part_boundary = 0; Assert(filter); @@ -624,28 +731,23 @@ bloom_contains_value(BloomFilter *filter, uint32 value) /* now the regular hashing mode */ Assert(BLOOM_IS_HASHED(filter)); - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + Assert(bitloc < filter->nbits); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); /* if the bit is not set, the value is not there */ if (! (filter->data[byte] & (0x01 << bit))) return false; /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } /* all hashes found in bloom filter */ @@ -1065,8 +1167,14 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS) if (BLOOM_IS_HASHED(filter)) { - appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + appendStringInfo(&str, + "mode: hashed nhashes: %u nbits: %u nbits_set: %u partition lengths: [", filter->nhashes, filter->nbits, filter->nbits_set); + for (int i = 0; i < filter->nhashes - 1; i++) + { + appendStringInfo(&str, "%u, ", filter->partlens[i]); + } + appendStringInfo(&str, "%u]", filter->partlens[filter->nhashes - 1]); } else {
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-24 23:50                                                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-24 23:50 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 24, 2020 at 05:18:03PM -0400, John Naylor wrote:
>On Mon, Sep 21, 2020 at 3:56 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>>
>> On Mon, Sep 21, 2020 at 01:42:34PM -0400, John Naylor wrote:
>
>> >While playing around with the numbers I had an epiphany: At the
>> >defaults, the filter already takes up ~4.3kB, over half the page.
>> >There is no room for another tuple, so if we're only indexing one
>> >column, we might as well take up the whole page.
>>
>> Hmm, yeah. I may be wrong but IIRC indexes don't support external
>> storage but compression is still allowed. So even if those defaults are
>> a bit higher than needed that should make the bloom filters a bit more
>> compressible, and thus fit multiple BRIN tuples on a single page.
>
>> Not sure about how much we want to rely on these optimizations, though,
>> considering multi-column indexes kinda break this.
>
>Yeah. Okay, then it sounds like we should go in the other direction,
>as the block comment at the top of brin_bloom.c implies. Indexes with
>multiple bloom-indexed columns already don't fit in one 8kB page, so I
>think every documented example should have a much lower
>pages_per_range. Using 32 pages per range with max tuples gives n =
>928. With default p, that's about 1.1 kB per brin tuple, so one brin
>page can index 224 pages, much more than with the default 128.
>
>Hmm, how ugly would it be to change the default range size depending
>on the opclass?
>

Not sure. What would happen for multi-column BRIN indexes with different
opclasses?

>If indexes don't support external storage, that sounds like a pain to
>add. Also, with very small fpr, you can easily get into many megabytes
>of filter space, which kind of defeats the purpose of brin in the
>first place.
>

True.

>There is already this item from the brin readme:
>
>* Different-size page ranges?
>  In the current design, each "index entry" in a BRIN index covers the same
>  number of pages.  There's no hard reason for this; it might make sense to
>  allow the index to self-tune so that some index entries cover smaller page
>  ranges, if this allows the summary values to be more compact.  This
>would incur
>  larger BRIN overhead for the index itself, but might allow better pruning of
>  page ranges during scan.  In the limit of one index tuple per page, the index
>  itself would occupy too much space, even though we would be able to skip
>  reading the most heap pages, because the summary values are tight; in the
>  opposite limit of a single tuple that summarizes the whole table, we wouldn't
>  be able to prune anything even though the index is very small.  This can
>  probably be made to work by using the range map as an index in itself.
>
>This sounds like a lot of work, but would be robust.
>

Yeah. I think it's a fairly independent / orthogonal project.

>Anyway, given that this is a general problem and not specific to the
>prime partition algorithm, I'll leave that out of the attached patch,
>named as a .txt to avoid confusing the cfbot.
>
>> >We could also generate the primes via a sieve instead, which is really
>> >fast (and more code). That would be more robust, but that would require
>> >the filter to store the actual primes used, so 20 more bytes at max k =
>> >10. We could hard-code space for that, or to keep from hard-coding
>> >maximum k and thus lowest possible false positive rate, we'd need more
>> >bookkeeping.
>> >
>>
>> I don't think the efficiency of this code matters too much - it's only
>> used once when creating the index, so the simpler the better. Certainly
>> for now, while testing the partitioning approach.
>
>To check my understanding, isn't bloom_init() called for every tuple?
>Agreed on simplicity so done this way.
>

No, it's only called for the first non-NULL value in the page range
(unless I made a boo boo when writing that code).

>> >So, with the partition approach, we'd likely have to set in stone
>> >either max nbits, or min target false positive rate. The latter option
>> >seems more principled, not only for the block size, but also since the
>> >target fp rate is already fixed by the reloption, and as I've
>> >demonstrated, we can often go above and beyond the reloption even
>> >without changing k.
>> >
>>
>> That seems like a rather annoying limitation, TBH.
>
>I don't think the latter is that bad. I've capped k at 10 for
>demonstration's sake.:
>
>(928 is from using 32 pages per range)
>
>n    k   m      p
>928   7  8895   0.01
>928  10  13343  0.001  (lowest p supported in patch set)
>928  13  17790  0.0001
>928  10  18280  0.0001 (still works with lower k, needs higher m)
>928  10  17790  0.00012 (even keeping m from row #3, capping k doesn't
>degrade p much)
>
>Also, k seems pretty robust against small changes as long as m isn't
>artificially constrained and as long as p is small.
>
>So I *think* it's okay to cap k at 10 or 12, and not bother with
>adjusting m, which worsens space issues. As I found before, lowering k
>raises target fpr, but seems more robust to overshooting ndistinct. In
>any case, we only need k * 2 bytes to store the partition lengths.
>
>The only way I see to avoid any limitation is to make the array of
>primes variable length, which could be done by putting the filter
>offset calculation into a macro. But having two variable-length arrays
>seems messy.
>

Hmmm. I wonder how would these limitations impact the conclusions from
the one-hashing paper? Or was this just for the sake of a demonstration?

I'd suggest we just do the simplest thing possible (be it a hard-coded
table of primes or a sieve) and then evaluate if we need to do something
more sophisticated.

>> >Hmm, I'm not sure I understand you. I can see not caring to trim wasted
>> >bits, but we can't set/read off the end of the filter. If we don't
>> >wrap, we could just skip reading/writing that bit. So a tiny portion of
>> >access would be at k - 1. The paper is not clear on what to do here,
>> >but they are explicit in minimizing the absolute value, which could go
>> >on either side.
>> >
>>
>> What I meant is that is that the paper says this:
>>
>>      Given a planned overall length mp for a Bloom filter, we usually
>>      cannot get k prime numbers to make their sum mf to be exactly mp. As
>>      long as the difference between mp and mf is small enough, it neither
>>      causes any trouble for the software implementation nor noticeably
>>      shifts the false positive ratio.
>>
>> Which I think means we can pick mp, generate k primes with sum mf close
>> to mp, and just use that with mf bits.
>
>Oh, I see. When I said "trim" I meant exactly that (when mf < mp).
>Yeah, we can bump it up as well for the other case. I've done it that
>way.
>

OK

>> >+ add_local_real_reloption(relopts, "false_positive_rate", + "desired
>> >false-positive rate for the bloom filters", +
>> >BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions,
>> >falsePositiveRate));
>> >
>> >When I set fp to 1.0, the reloption code is okay with that, but then
>> >later the assertion gets triggered.
>> >
>>
>> Hmm, yeah. I wonder what to do about that, considering indexes with fp
>> 1.0 are essentially useless.
>
>Not just useless -- they're degenerate. When p = 1.0, m = k = 0 -- We
>cannot accept this value from the user. Looking up thread, 0.1 was
>suggested as a limit. That might be a good starting point.
>

Makes sense, I'll fix it that way.

>This is interesting work! Having gone this far, I'm going to put more
>attention to the multi-minmax patch and actually start performance
>testing.
>

Cool, thanks! I'll take a look at your one-hashing patch tomorrow.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-28 20:42                                                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-28 20:42 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Thu, Sep 24, 2020 at 7:50 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
>
> On Thu, Sep 24, 2020 at 05:18:03PM -0400, John Naylor wrote:

> >Hmm, how ugly would it be to change the default range size depending
> >on the opclass?
> >
>
> Not sure. What would happen for multi-column BRIN indexes with different
> opclasses?

Sounds like a can of worms. In any case I suspect if there is no more
graceful way to handle too-large filters than ERROR out the first time
trying to write to the index, this feature might meet some resistance.
Not sure what to suggest, though.

> >> I don't think the efficiency of this code matters too much - it's only
> >> used once when creating the index, so the simpler the better. Certainly
> >> for now, while testing the partitioning approach.
> >
> >To check my understanding, isn't bloom_init() called for every tuple?
> >Agreed on simplicity so done this way.
> >
>
> No, it's only called for the first non-NULL value in the page range
> (unless I made a boo boo when writing that code).

Ok, then I basically understood -- by tuple I meant BRIN tuple, pardon
my ambiguity. After thinking about it, I agree that CPU cost is
probably trivial (and if not, something is seriously wrong).

> >n    k   m      p
> >928   7  8895   0.01
> >928  10  13343  0.001  (lowest p supported in patch set)
> >928  13  17790  0.0001
> >928  10  18280  0.0001 (still works with lower k, needs higher m)
> >928  10  17790  0.00012 (even keeping m from row #3, capping k doesn't
> >degrade p much)
> >
> >Also, k seems pretty robust against small changes as long as m isn't
> >artificially constrained and as long as p is small.
> >
> >So I *think* it's okay to cap k at 10 or 12, and not bother with
> >adjusting m, which worsens space issues. As I found before, lowering k
> >raises target fpr, but seems more robust to overshooting ndistinct. In
> >any case, we only need k * 2 bytes to store the partition lengths.
> >
> >The only way I see to avoid any limitation is to make the array of
> >primes variable length, which could be done by putting the filter
> >offset calculation into a macro. But having two variable-length arrays
> >seems messy.
> >
>
> Hmmm. I wonder how would these limitations impact the conclusions from
> the one-hashing paper? Or was this just for the sake of a demonstration?

Using "10" in the patch is a demonstration, which completely supports
the current fpr allowed by the reloption, and showing what happens if
fpr is allowed to go lower. But for your question, I *think* this
consideration is independent from the conclusions. The n, k, m values
give a theoretical false positive rate, assuming a completely perfect
hashing scheme. The numbers I'm playing with show consequences in the
theoretical fpr. The point of the paper (and others like it) is how to
get the real fpr as close as possible to the fpr predicted by the
theory. My understanding anyway.

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-09-29 02:12                                                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-09-29 02:12 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Mon, Sep 28, 2020 at 04:42:39PM -0400, John Naylor wrote:
>On Thu, Sep 24, 2020 at 7:50 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>>
>> On Thu, Sep 24, 2020 at 05:18:03PM -0400, John Naylor wrote:
>
>> >Hmm, how ugly would it be to change the default range size depending
>> >on the opclass?
>> >
>>
>> Not sure. What would happen for multi-column BRIN indexes with different
>> opclasses?
>
>Sounds like a can of worms. In any case I suspect if there is no more
>graceful way to handle too-large filters than ERROR out the first time
>trying to write to the index, this feature might meet some resistance.
>Not sure what to suggest, though.
>

Is it actually all that different from the existing BRIN indexes?
Consider this example:

create table x (a text, b text, c text);

create index on x using brin (a,b,c);

create or replace function random_str(p_len int) returns text as $$
select string_agg(x, '') from (select chr(1 + (254 * random())::int ) as x from generate_series(1,$1)) foo;
$$ language sql;

test=# insert into x select random_str(1000), random_str(1000), random_str(1000);
ERROR:  index row size 9056 exceeds maximum 8152 for index "x_a_b_c_idx"


I'm a bit puzzled, though, because both of these things seem to work:

1) insert before creating the index

create table x (a text, b text, c text);
insert into x select random_str(1000), random_str(1000), random_str(1000);
create index on x using brin (a,b,c);
-- and there actually is a non-empty summary with real data
select * from brin_page_items(get_raw_page('x_a_b_c_idx', 2), 'x_a_b_c_idx'::regclass);


2) insert "small" row before inserting the over-sized one

create table x (a text, b text, c text);
insert into x select random_str(10), random_str(10), random_str(10);
insert into x select random_str(1000), random_str(1000), random_str(1000);
create index on x using brin (a,b,c);
-- and there actually is a non-empty summary with the "big" values
select * from brin_page_items(get_raw_page('x_a_b_c_idx', 2), 'x_a_b_c_idx'::regclass);


I find this somewhat strange - how come we don't fail here too?


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-09-30 11:57                                                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-09-30 11:57 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Mon, Sep 28, 2020 at 10:12 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:

> Is it actually all that different from the existing BRIN indexes?
> Consider this example:
>
> create table x (a text, b text, c text);
>
> create index on x using brin (a,b,c);
>
> create or replace function random_str(p_len int) returns text as $$
> select string_agg(x, '') from (select chr(1 + (254 * random())::int ) as x from generate_series(1,$1)) foo;
> $$ language sql;
>
> test=# insert into x select random_str(1000), random_str(1000), random_str(1000);
> ERROR:  index row size 9056 exceeds maximum 8152 for index "x_a_b_c_idx"

Hmm, okay. As for which comes first, insert or index creation, I'm
baffled, too. I also would expect the example above would take up a
bit over 6000 bytes, but not 9000.

-- 
John Naylor                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
@ 2020-10-01 18:41                                                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-10-01 18:41 UTC (permalink / raw)
  To: John Naylor <john.naylor@2ndquadrant.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers

On Wed, Sep 30, 2020 at 07:57:19AM -0400, John Naylor wrote:
>On Mon, Sep 28, 2020 at 10:12 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>
>> Is it actually all that different from the existing BRIN indexes?
>> Consider this example:
>>
>> create table x (a text, b text, c text);
>>
>> create index on x using brin (a,b,c);
>>
>> create or replace function random_str(p_len int) returns text as $$
>> select string_agg(x, '') from (select chr(1 + (254 * random())::int ) as x from generate_series(1,$1)) foo;
>> $$ language sql;
>>
>> test=# insert into x select random_str(1000), random_str(1000), random_str(1000);
>> ERROR:  index row size 9056 exceeds maximum 8152 for index "x_a_b_c_idx"
>
>Hmm, okay. As for which comes first, insert or index creation, I'm
>baffled, too. I also would expect the example above would take up a
>bit over 6000 bytes, but not 9000.
>

OK, so this seems like a data corruption bug in BRIN, actually.

The ~9000 bytes is actually about right, because the strings are in
UTF-8 so roughly 1.5B per character seems about right. And we have 6
values to store (3 columns, min/max for each), so 6 * 1500 = 9000.

The real question is how come INSERT + CREATE INDEX actually manages to
create an index tuple. And the answer is pretty simple - brin_form_tuple
kinda ignores toasting, happily building index tuples where some values
are toasted.

Consider this:

     create table x (a text, b text, c text);
     insert into x select random_str(1000), random_str(1000), random_str(1000);

     create index on x using brin (a,b,c);
     delete from x;
     vacuum x;

     set enable_seqscan=off;

     insert into x select random_str(10), random_str(10), random_str(10);
     ERROR:  missing chunk number 0 for toast value 16530 in pg_toast_16525

     explain analyze select * from x where a = 'xxx';
     ERROR:  missing chunk number 0 for toast value 16530 in pg_toast_16525

     select * from brin_page_items(get_raw_page('x_a_b_c_idx', 2), 'x_a_b_c_idx'::regclass);
     ERROR:  missing chunk number 0 for toast value 16547 in pg_toast_16541


Interestingly enough, running the select before the insert seems to be
working - not sure why.

Anyway, it behaves like this since 9.5 :-(


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-10-01 22:21                                                                                                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  0 siblings, 1 reply; 177+ messages in thread

From: Alvaro Herrera @ 2020-10-01 22:21 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: John Naylor <john.naylor@2ndquadrant.com>; pgsql-hackers

On 2020-Oct-01, Tomas Vondra wrote:

> OK, so this seems like a data corruption bug in BRIN, actually.

Oh crap.  You're right -- the data needs to be detoasted before being
put in the index.

I'll have a look at how this can be fixed.





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2020-11-02 18:05                                                                                                                                                           ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Anastasia Lubennikova @ 2020-11-02 18:05 UTC (permalink / raw)
  To: pgsql-hackers@lists.postgresql.org; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>

Status update for a commitfest entry.

According to cfbot the patch no longer compiles.
Tomas, can you send an update, please?

I also see that a few last messages mention a data corruption bug. Sounds pretty serious.
Alvaro, have you had a chance to look at it? I don't see anything committed yet, nor any active discussion in other threads.

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
@ 2020-11-02 23:25                                                                                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-11-02 23:25 UTC (permalink / raw)
  To: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; +Cc: pgsql-hackers@lists.postgresql.org

On Mon, Nov 02, 2020 at 06:05:27PM +0000, Anastasia Lubennikova wrote:
>Status update for a commitfest entry.
>
>According to cfbot the patch no longer compiles.  Tomas, can you send
>an update, please?
>

Yep, here's an updated patch series. It got broken by f90149e6285aa
which disallowed OID macros in pg_type, but fixing it was simple.

I've also included the patch adopting the one-hash bloom, as implemented
by John Naylor. I didn't have time to do any testing / evaluation yet,
so I've kept it as a separate part - ultimately we should either merge
it into the other bloom patch or discard it.

>I also see that a few last messages mention a data corruption bug.
>Sounds pretty serious.  Alvaro, have you had a chance to look at it? I
>don't see anything committed yet, nor any active discussion in other
>threads.

Yeah, I'm not aware of any fix addressing this - my understanding was
Alvaro plans to handle that, but amybe I misinterpreted his response.
Anyway, I think the fix is simple - we need to de-TOAST the data while
adding the data to index, and we need to consider what to do with
existing possibly-broken indexes.


regards

-- 
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services 

Attachments: download all (7 files, .tar.gz)

0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20201103.patch (text/plain, 23.5K) download From bd8a32967b41c4c4efc0b7df54650c82a683e357 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/7] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..b233558310 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index d9770bbadd..8f0f76601b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8123,7 +8123,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8139,7 +8139,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.26.2
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20201103.patch (text/plain, 23.3K) download From 18195ac9ded163bac8fdf0d32609082e283b62b2 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 17 Sep 2020 17:26:10 +0200 Subject: [PATCH 2/7] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit, and it simplifies the support procedures quite a bit, as they don't need to care about NULL values and flags at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 294 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 240 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..f438e59c75 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys) return scan; } + /* * Execute the index scan. * @@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +542,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +735,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +763,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1543,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1629,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index b233558310..8886ad984e 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
0003-optimize-allocations-20201103.patch (text/plain, 3.9K) download From 58f920a5346420eb93934443983794a286391dd3 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/7] optimize allocations --- src/backend/access/brin/brin.c | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index f438e59c75..bd052c8b0b 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -373,6 +373,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; + char *tmp PG_USED_FOR_ASSERTS_ONLY; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -398,11 +401,50 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + tmp = ptr; + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + Assert(tmp + len == ptr); + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -438,9 +480,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) { FmgrInfo *tmp; - /* No key/null arrays for this attribute. */ - Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); - Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); + /* First time we see this attribute, so no key/null keys. */ + Assert(nkeys[keyattno - 1] == 0); + Assert(nnullkeys[keyattno - 1] == 0); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -451,17 +493,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.26.2
0004-BRIN-bloom-indexes-20201103.patch (text/plain, 138.7K) download From a5ad2fbd4b7e3a1797e6a8903c55e99dd283a29b Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:43 +0200 Subject: [PATCH 4/7] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2965 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 749db2845e..fea13791d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -559,6 +559,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..977d11d790 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1107 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * The 1% value is mostly arbitrary, it just looks nice. + */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_BLOOM_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 1dfb6fd373..12033d48ec 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1705,6 +1705,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1722,6 +1727,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1739,6 +1749,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1885,6 +1900,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1902,6 +1955,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1919,6 +1977,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1936,6 +1999,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -2002,6 +2070,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2019,6 +2101,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2036,6 +2123,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2053,6 +2145,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2090,6 +2187,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2107,6 +2209,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2253,6 +2360,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2270,6 +2415,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2287,6 +2437,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2338,6 +2493,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2355,6 +2515,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2416,6 +2581,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index a8e0c4ff8a..15f3baea15 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -772,6 +772,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -785,6 +803,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -798,6 +834,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -899,6 +953,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -912,6 +1018,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -924,6 +1048,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -936,6 +1077,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -986,6 +1144,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1000,6 +1197,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1014,6 +1231,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1027,6 +1264,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1061,6 +1316,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1074,6 +1349,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1181,6 +1474,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1195,6 +1544,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1209,6 +1578,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1249,6 +1638,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1262,6 +1671,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1297,6 +1724,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 8f0f76601b..c746efd33b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8146,6 +8146,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -10999,3 +11019,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 21a467a7a7..59da6a9804 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -621,3 +621,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..d58a4a483c --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +ERROR: value 0.0 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); +ERROR: value 1.01 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "1.000000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 7825a765cd..ef0f19f9f6 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2021,6 +2021,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2084,7 +2085,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(125 rows) +(126 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 7a46a13252..a8e677dd0b 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 9a80b80f73..5776dbdd73 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..abd5a33deb --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 1.01) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.26.2
0005-use-one-hash-bloom-variant-20201103.patch (text/plain, 8.5K) download From b33e0c4e93226ba665b6087a5b86c348079fdbbc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Mon, 2 Nov 2020 23:55:28 +0100 Subject: [PATCH 5/7] use one-hash bloom variant --- src/backend/access/brin/brin_bloom.c | 176 +++++++++++++++++++++------ 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 977d11d790..b5f07954a6 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -203,6 +203,9 @@ typedef struct BloomOptions */ #define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ +/* With the minimum allowed false positive rate of 0.001, we need up to 10 hashes */ +#define BLOOM_MAX_NUM_PARTITIONS 10 + #define BloomGetNDistinctPerRange(opts) \ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ (((BloomOptions *) (opts))->nDistinctPerRange) : \ @@ -270,6 +273,7 @@ typedef struct BloomFilter uint8 nhashes; /* number of hash functions */ uint32 nbits; /* number of bits in the bitmap (size) */ uint32 nbits_set; /* number of bits set to 1 */ + uint16 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ /* data of the bloom filter (used both for sorted and hashed phase) */ char data[FLEXIBLE_ARRAY_MEMBER]; @@ -279,6 +283,114 @@ typedef struct BloomFilter static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); +/* + * generate_primes + * returns array of all primes less than limit + * + * WIP: very naive prime sieve; could be optimized using segmented ranges + */ +static uint16 * +generate_primes(int limit) +{ + /* upper bound of number of primes below limit */ + /* WIP: reference for this number */ + int numprimes = 1.26 * limit / log(limit); + + bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); + uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + + int maxfactor = floor(sqrt(limit)); + int factor = 2; /* first prime */ + + /* mark the sieve where the index is composite */ + while (factor < maxfactor) + { + for (int i = factor * factor; i < limit; i += factor) + is_composite[i] = true; + do { factor++; } while (is_composite[factor]); + } + + /* the unmarked numbers are prime, so copy over */ + for (int i = 2, j = 0; i < limit && j < numprimes; i++) + { + if (!is_composite[i]) + primes[j++] = i; + } + + /* there should still be some zeroes at the end, but make sure */ + primes[numprimes - 1] = 0; + + /* pretty large, so free it now (segmented ranges would make it smaller) */ + pfree(is_composite); + return primes; +} + +/* + * set_bloom_partitions + * Calculate k moduli for one-hashing bloom filter. + * + * Find consecutive primes whose sum is close to nbits and + * return the sum. Copy the primes to the filter to use as + * partition lengths. + * WIP: one-hashing bf paper ref somewhere + */ +static uint32 +set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) +{ + int min, diff, incr; + int pidx = 0; + int sum = 0; + int target_partlen = nbits / nhashes; + + /* + * Increase the limit to ensure we have some primes higher than + * the target partition length. The 100 value is arbitrary, but + * should be well over what we need. + */ + uint16 *primes = generate_primes(target_partlen + 100); + + /* + * In our array of primes, find a sequence of length nhashes, whose + * last item is close to our target partition length. The end of the + * array will be filled with zeros, so we need to guard against that. + */ + while (primes[pidx + nhashes - 1] <= target_partlen && + primes[pidx] > 0) + pidx++; + + for (int i = 0; i < nhashes; i++) + sum += primes[pidx + i]; + + /* + * Since all the primes are less than or equal the desired partition + * length, the sum is somewhat less than nbits. Increment the starting + * point until we find the sequence of primes whose sum is closest to + * nbits. It doesn't matter whether it's higher or lower. + */ + min = abs(nbits - sum); + for (;;) + { + incr = primes[pidx + nhashes] - primes[pidx]; + diff = abs(nbits - (sum + incr)); + if (diff >= min) + break; + + min = diff; + sum += incr; + pidx++; + } + + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); + + /* WIP: assuming it's not important to pfree primes */ + + /* + * The actual filter length will be the sum of the partition lengths + * rounded up to the nearest byte. + */ + return (uint32) ((sum + 7) / 8) * 8; +} + /* * bloom_init * Initialize the Bloom Filter, allocate all the memory. @@ -301,15 +413,13 @@ bloom_init(int ndistinct, double false_positive_rate) m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); - /* round m to whole bytes */ - m = ((m + 7) / 8) * 8; - /* * round(log(2.0) * m / ndistinct), but assume round() may not be * available on Windows */ k = log(2.0) * m / ndistinct; k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + k = Min(k, BLOOM_MAX_NUM_PARTITIONS); /* * Allocate the bloom filter with a minimum size 64B (about 40B in the @@ -323,7 +433,9 @@ bloom_init(int ndistinct, double false_positive_rate) filter->flags = 0; /* implies SORTED phase */ filter->nhashes = (int) k; - filter->nbits = m; + + /* calculate the partition lengths and adjust m to match */ + filter->nbits = set_bloom_partitions(m, k, filter->partlens); SET_VARSIZE(filter, len); @@ -445,7 +557,7 @@ static BloomFilter * bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) { int i; - uint32 big_h, h, d; + int part_boundary = 0; /* assume 'not updated' by default */ Assert(filter); @@ -514,17 +626,16 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) /* we better be in the hashing phase */ Assert(BLOOM_IS_HASHED(filter)); - /* compute the hashes, used for the bloom filter */ - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + Assert(bitloc < filter->nbits); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); /* if the bit is not set, set it and remember we did that */ if (! (filter->data[byte] & (0x01 << bit))) @@ -536,12 +647,7 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) } /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } return filter; @@ -571,6 +677,7 @@ bloom_switch_to_hashing(BloomFilter *filter) newfilter->nhashes = filter->nhashes; newfilter->nbits = filter->nbits; + memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint16)); newfilter->flags |= BLOOM_FLAG_PHASE_HASH; SET_VARSIZE(newfilter, len); @@ -595,7 +702,7 @@ static bool bloom_contains_value(BloomFilter *filter, uint32 value) { int i; - uint32 big_h, h, d; + int part_boundary = 0; Assert(filter); @@ -624,28 +731,23 @@ bloom_contains_value(BloomFilter *filter, uint32 value) /* now the regular hashing mode */ Assert(BLOOM_IS_HASHED(filter)); - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + Assert(bitloc < filter->nbits); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); /* if the bit is not set, the value is not there */ if (! (filter->data[byte] & (0x01 << bit))) return false; /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } /* all hashes found in bloom filter */ @@ -1064,8 +1166,14 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS) if (BLOOM_IS_HASHED(filter)) { - appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + appendStringInfo(&str, + "mode: hashed nhashes: %u nbits: %u nbits_set: %u partition lengths: [", filter->nhashes, filter->nbits, filter->nbits_set); + for (int i = 0; i < filter->nhashes - 1; i++) + { + appendStringInfo(&str, "%u, ", filter->partlens[i]); + } + appendStringInfo(&str, "%u]", filter->partlens[filter->nhashes - 1]); } else { -- 2.26.2
0006-BRIN-minmax-multi-indexes-20201103.patch (text/plain, 217.2K) download From ff72079ced7ba5bfd101b852a6dd13cdcd582f39 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:49 +0200 Subject: [PATCH 6/7] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2389 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4792 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index fea13791d6..660769ef5e 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -590,6 +590,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..f9a7ae6608 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2389 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_MINMAX_MULTI_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 46e6b23c87..8bdf837070 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + for (datumno = 0; datumno < brdesc->bd_info[keyno]->oi_nstored; datumno++) @@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 12033d48ec..cda1ad4fbe 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1900,6 +1900,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1977,6 +2123,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -2003,6 +2166,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2070,6 +2249,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2101,6 +2346,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2123,6 +2385,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2145,6 +2424,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2209,6 +2505,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2360,6 +2673,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2415,6 +2874,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2437,6 +2913,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2493,6 +2986,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2515,6 +3025,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2581,6 +3108,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 15f3baea15..8d212ede8b 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -953,6 +953,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1048,6 +1194,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1094,6 +1257,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1144,6 +1324,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1197,6 +1451,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1231,6 +1505,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1264,6 +1558,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1349,6 +1663,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1474,6 +1807,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1544,6 +2041,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1578,6 +2095,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1638,6 +2175,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1669,7 +2226,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1724,6 +2302,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index c746efd33b..b0dd5f5040 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8129,6 +8129,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11033,3 +11098,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 59da6a9804..af8d010c69 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -628,3 +628,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index a8e677dd0b..0e3cadd054 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 5776dbdd73..dc2a9ce36c 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -109,6 +109,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.26.2
0007-Ignore-correlation-for-new-BRIN-opclasses-20201103.patch (text/plain, 4.1K) download From 6c6ad41abbcfcb37ba13f3df8b0e5dceacb845cc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 7/7] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index b5f07954a6..769feb6b3d 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -783,6 +783,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index f9a7ae6608..4f6262f241 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index bec357fcef..d84bf4726c 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2020-11-07 20:38                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-08 18:21                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  0 siblings, 2 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-11-07 20:38 UTC (permalink / raw)
  To: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; +Cc: pgsql-hackers@lists.postgresql.org

Hi,

Here's a rebased version of the patch series, to keep the cfbot happy.
I've also restricted the false positive rate to [0.001, 0.1] instead of
the original [0.0, 1.0], per discussion on this thread.


I've done a bunch of experiments, comparing the "regular" bloom indexes
with the one-hashing scheme proposed by John Naylor. I've been wondering
if there's some measurable difference, especially in:

* efficiency (query duration)

* false positive rate depending on the fill factor

So I ran a bunch of tests on synthetic data sets, varying parameters
affecting the BRIN bloom indexes:

1) different pages_per_range

2) number of distinct values per range

3) fill factor of the bloom filter (66%, 100%, 200%)

Attached is a script I used to test this, and a simple spreadsheet
summarizing the results, comparing the results for each combination of
parameters. For each combination it shows average query duration (over
10 runs) and scan fraction (what fraction of table was scanned).

Overall, I think there's very little difference, particularly in the
"match" cases when we're searching for a value that we know is in the
table. The one-hash variant seems to perform a bit better, but the
difference is fairly small.

In the "mismatch" cases (searching for value that is not in the table)
the differences are more significant, but it might be noise. It does
seem much more "green" than "red", i.e. the one-hash variant seems to be
faster (although this does depend on the values for formatting).

To sum this up, I think the one-hash approach seems interesting. It's
not going to give us huge speedups because we're only hashing int32
values anyway (not the source data), but it's worth exploring.


I've started looking at the one-hash code changes, and I've discovered a
couple issues. I've been wondering how expensive the naive prime sieve
is - it's not extremely hot code path, as we're only running it for each
page range. But still. So my plan was to create the largest bloom filter
possible, and see how much time generate_primes() takes.

So I initialized a cluster with 32kB blocks and tried to do this:

  create index on t
  using brin (a int4_bloom_ops(n_distinct_per_range=120000,
                               false_positive_rate=0.1));

which ends up using nbits=575104 (which is 2x the page size, but let's
ignore that) and nhashes=3. That however crashes and burns, because:

a) set_bloom_partitions does this:

    while (primes[pidx + nhashes - 1] <= target && primes[pidx] > 0)
       pidx++;

which is broken, because the second part of the condition only checks
the current index - we may end up using nhashes primes after that, and
some of them may be 0. So this needs to be:

    while (primes[pidx + nhashes - 1] <= target &&
           primes[pidx + nhashes] > 0)
       pidx++;

(We know there's always at least one 0 at the end, so it's OK not to
check the length explicitly.)

b) set_bloom_partitions does this to generate primes:

    /*
     * Increase the limit to ensure we have some primes higher than
     * the target partition length. The 100 value is arbitrary, but
     * should be well over what we need.
     */
    primes = generate_primes(target_partlen + 100);

It's not clear to me why 100 is sufficient, particularly for large page
sizes. AFAIK the primes get more and more sparse, so how does this
guarantee we'll get enough "sufficiently large" primes?


c) generate_primes uses uint16 to store the primes, so it can only
generate primes up to 32768. That's (probably) enough for 8kB pages, but
for 32kB pages it's clearly insufficient.


I've fixes these issues in a separate WIP patch, with some simple
debugging logging.


As for the original question how expensive this naive sieve is, I
haven't been able to measure any significant timings. The logging aroung
generate_primes usually looks like this:

2020-11-07 20:36:10.614 CET [232789] LOG:  generating primes nbits
575104 nhashes 3 target_partlen 191701
2020-11-07 20:36:10.614 CET [232789] LOG:  primes generated

So it takes 0.000 second for this extreme page size. I don't think we
need to invent anything more elaborate.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments: download all (10 files, .tar.gz)

  i5.ods (application/vnd.oasis.opendocument.spreadsheet, 95.4K) download

0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20201107.patch (text/x-patch, 23.5K) download From 83eb4a6291d80e5240f775c3057f0ecb54795d1d Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/8] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..b233558310 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index c01da4bf01..bcbbc97de0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8137,7 +8137,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8153,7 +8153,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.26.2
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20201107.patch (text/x-patch, 23.3K) download From 1e63b78e2c391363ba326d4fbcb5348b024854a2 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 17 Sep 2020 17:26:10 +0200 Subject: [PATCH 2/8] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit - especially the support procedures quite a bit, as they don't need to care about NULL values and flags at all. It also means the IS [NOT] NULL clauses can be evaluated without invoking the support procedure at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 293 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 239 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..4dd29c7b10 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -389,8 +367,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +395,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +422,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +447,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +541,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +734,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +762,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1542,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1628,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index b233558310..8886ad984e 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
0003-Optimize-allocations-in-bringetbitmap-20201107.patch (text/x-patch, 4.4K) download From 96e0faa23597a41d34a0cb66e7074dc1a64c3632 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/8] Optimize allocations in bringetbitmap The bringetbitmap function allocates memory for various purposes, which may be quite expensive, depending on the number of scan keys. Instead of allocating them separately, allocate one bit chunk of memory an carve it into smaller pieces as needed - all the pieces have the same lifespan, and it saves quite a bit of CPU and memory overhead. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 4dd29c7b10..470431010d 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -372,6 +372,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; + char *tmp PG_USED_FOR_ASSERTS_ONLY; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -397,11 +400,50 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + tmp = ptr; + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + Assert(tmp + len == ptr); + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -437,9 +479,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) { FmgrInfo *tmp; - /* No key/null arrays for this attribute. */ - Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); - Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); + /* First time we see this attribute, so no key/null keys. */ + Assert(nkeys[keyattno - 1] == 0); + Assert(nnullkeys[keyattno - 1] == 0); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -450,17 +492,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.26.2
0004-BRIN-bloom-indexes-20201107.patch (text/x-patch, 138.9K) download From a51aaac6de42b16078cfe549e60246ac51fda328 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 15:21:18 +0100 Subject: [PATCH 4/8] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1112 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2970 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 749db2845e..fea13791d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -559,6 +559,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..52b3d477c8 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1112 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * Allowed range and default value for the false positive range. The exact + * values are somewhat arbitrary. + */ +#define BLOOM_MIN_FALSE_POSITIVE_RATE 0.001 /* 0.1% fp rate */ +#define BLOOM_MAX_FALSE_POSITIVE_RATE 0.1 /* 10% fp rate */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_BLOOM_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + BLOOM_MIN_FALSE_POSITIVE_RATE, + BLOOM_MAX_FALSE_POSITIVE_RATE, + offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index bbe357fbc0..3c0df5597f 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1689,6 +1689,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1706,6 +1711,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1723,6 +1733,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1869,6 +1884,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1886,6 +1939,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1903,6 +1961,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1920,6 +1983,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -1986,6 +2054,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2003,6 +2085,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2020,6 +2107,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2037,6 +2129,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2074,6 +2171,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2091,6 +2193,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2237,6 +2344,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2254,6 +2399,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2271,6 +2421,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2322,6 +2477,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2339,6 +2499,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2400,6 +2565,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index a8e0c4ff8a..15f3baea15 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -772,6 +772,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -785,6 +803,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -798,6 +834,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -899,6 +953,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -912,6 +1018,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -924,6 +1048,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -936,6 +1077,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -986,6 +1144,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1000,6 +1197,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1014,6 +1231,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1027,6 +1264,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1061,6 +1316,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1074,6 +1349,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1181,6 +1474,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1195,6 +1544,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1209,6 +1578,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1249,6 +1638,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1262,6 +1671,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1297,6 +1724,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bcbbc97de0..7351ecb07e 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8160,6 +8160,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -11006,3 +11026,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 21a467a7a7..59da6a9804 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -621,3 +621,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..19b866283a --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0009) +); +ERROR: value 0.0009 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "0.100000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.11) +); +ERROR: value 0.11 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "0.100000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 7ed29b4961..9b5715da86 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2018,6 +2018,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2081,7 +2082,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(123 rows) +(124 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index ae89ed7f0b..69c39a1ca6 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 525bdc804f..6388bc7843 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..3c2ef56316 --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0009) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.11) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.26.2
0005-use-one-hash-bloom-variant-20201107.patch (text/x-patch, 8.5K) download From 2055c7aa983ccd8d2993dd5219b90cb5f7314a37 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Mon, 2 Nov 2020 23:55:28 +0100 Subject: [PATCH 5/8] use one-hash bloom variant --- src/backend/access/brin/brin_bloom.c | 176 +++++++++++++++++++++------ 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 52b3d477c8..73f8478b4c 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -206,6 +206,9 @@ typedef struct BloomOptions #define BLOOM_MAX_FALSE_POSITIVE_RATE 0.1 /* 10% fp rate */ #define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ +/* With the minimum allowed false positive rate of 0.001, we need up to 10 hashes */ +#define BLOOM_MAX_NUM_PARTITIONS 10 + #define BloomGetNDistinctPerRange(opts) \ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ (((BloomOptions *) (opts))->nDistinctPerRange) : \ @@ -273,6 +276,7 @@ typedef struct BloomFilter uint8 nhashes; /* number of hash functions */ uint32 nbits; /* number of bits in the bitmap (size) */ uint32 nbits_set; /* number of bits set to 1 */ + uint16 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ /* data of the bloom filter (used both for sorted and hashed phase) */ char data[FLEXIBLE_ARRAY_MEMBER]; @@ -282,6 +286,114 @@ typedef struct BloomFilter static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); +/* + * generate_primes + * returns array of all primes less than limit + * + * WIP: very naive prime sieve; could be optimized using segmented ranges + */ +static uint16 * +generate_primes(int limit) +{ + /* upper bound of number of primes below limit */ + /* WIP: reference for this number */ + int numprimes = 1.26 * limit / log(limit); + + bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); + uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + + int maxfactor = floor(sqrt(limit)); + int factor = 2; /* first prime */ + + /* mark the sieve where the index is composite */ + while (factor < maxfactor) + { + for (int i = factor * factor; i < limit; i += factor) + is_composite[i] = true; + do { factor++; } while (is_composite[factor]); + } + + /* the unmarked numbers are prime, so copy over */ + for (int i = 2, j = 0; i < limit && j < numprimes; i++) + { + if (!is_composite[i]) + primes[j++] = i; + } + + /* there should still be some zeroes at the end, but make sure */ + primes[numprimes - 1] = 0; + + /* pretty large, so free it now (segmented ranges would make it smaller) */ + pfree(is_composite); + return primes; +} + +/* + * set_bloom_partitions + * Calculate k moduli for one-hashing bloom filter. + * + * Find consecutive primes whose sum is close to nbits and + * return the sum. Copy the primes to the filter to use as + * partition lengths. + * WIP: one-hashing bf paper ref somewhere + */ +static uint32 +set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) +{ + int min, diff, incr; + int pidx = 0; + int sum = 0; + int target_partlen = nbits / nhashes; + + /* + * Increase the limit to ensure we have some primes higher than + * the target partition length. The 100 value is arbitrary, but + * should be well over what we need. + */ + uint16 *primes = generate_primes(target_partlen + 100); + + /* + * In our array of primes, find a sequence of length nhashes, whose + * last item is close to our target partition length. The end of the + * array will be filled with zeros, so we need to guard against that. + */ + while (primes[pidx + nhashes - 1] <= target_partlen && + primes[pidx] > 0) + pidx++; + + for (int i = 0; i < nhashes; i++) + sum += primes[pidx + i]; + + /* + * Since all the primes are less than or equal the desired partition + * length, the sum is somewhat less than nbits. Increment the starting + * point until we find the sequence of primes whose sum is closest to + * nbits. It doesn't matter whether it's higher or lower. + */ + min = abs(nbits - sum); + for (;;) + { + incr = primes[pidx + nhashes] - primes[pidx]; + diff = abs(nbits - (sum + incr)); + if (diff >= min) + break; + + min = diff; + sum += incr; + pidx++; + } + + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); + + /* WIP: assuming it's not important to pfree primes */ + + /* + * The actual filter length will be the sum of the partition lengths + * rounded up to the nearest byte. + */ + return (uint32) ((sum + 7) / 8) * 8; +} + /* * bloom_init * Initialize the Bloom Filter, allocate all the memory. @@ -304,15 +416,13 @@ bloom_init(int ndistinct, double false_positive_rate) m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); - /* round m to whole bytes */ - m = ((m + 7) / 8) * 8; - /* * round(log(2.0) * m / ndistinct), but assume round() may not be * available on Windows */ k = log(2.0) * m / ndistinct; k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + k = Min(k, BLOOM_MAX_NUM_PARTITIONS); /* * Allocate the bloom filter with a minimum size 64B (about 40B in the @@ -326,7 +436,9 @@ bloom_init(int ndistinct, double false_positive_rate) filter->flags = 0; /* implies SORTED phase */ filter->nhashes = (int) k; - filter->nbits = m; + + /* calculate the partition lengths and adjust m to match */ + filter->nbits = set_bloom_partitions(m, k, filter->partlens); SET_VARSIZE(filter, len); @@ -448,7 +560,7 @@ static BloomFilter * bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) { int i; - uint32 big_h, h, d; + int part_boundary = 0; /* assume 'not updated' by default */ Assert(filter); @@ -517,17 +629,16 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) /* we better be in the hashing phase */ Assert(BLOOM_IS_HASHED(filter)); - /* compute the hashes, used for the bloom filter */ - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); + + Assert(bitloc < filter->nbits); /* if the bit is not set, set it and remember we did that */ if (! (filter->data[byte] & (0x01 << bit))) @@ -539,12 +650,7 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) } /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } return filter; @@ -574,6 +680,7 @@ bloom_switch_to_hashing(BloomFilter *filter) newfilter->nhashes = filter->nhashes; newfilter->nbits = filter->nbits; + memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint16)); newfilter->flags |= BLOOM_FLAG_PHASE_HASH; SET_VARSIZE(newfilter, len); @@ -598,7 +705,7 @@ static bool bloom_contains_value(BloomFilter *filter, uint32 value) { int i; - uint32 big_h, h, d; + int part_boundary = 0; Assert(filter); @@ -627,28 +734,23 @@ bloom_contains_value(BloomFilter *filter, uint32 value) /* now the regular hashing mode */ Assert(BLOOM_IS_HASHED(filter)); - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); + + Assert(bitloc < filter->nbits); /* if the bit is not set, the value is not there */ if (! (filter->data[byte] & (0x01 << bit))) return false; /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } /* all hashes found in bloom filter */ @@ -1069,8 +1171,14 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS) if (BLOOM_IS_HASHED(filter)) { - appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + appendStringInfo(&str, + "mode: hashed nhashes: %u nbits: %u nbits_set: %u partition lengths: [", filter->nhashes, filter->nbits, filter->nbits_set); + for (int i = 0; i < filter->nhashes - 1; i++) + { + appendStringInfo(&str, "%u, ", filter->partlens[i]); + } + appendStringInfo(&str, "%u]", filter->partlens[filter->nhashes - 1]); } else { -- 2.26.2
0006-one-hash-tweaks-20201107.patch (text/x-patch, 2.6K) download From f217c117b3a759b11ac3665c368e6353d9b0f5d9 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 20:36:38 +0100 Subject: [PATCH 6/8] one-hash tweaks --- src/backend/access/brin/brin_bloom.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 73f8478b4c..f6e65c0157 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -292,7 +292,7 @@ static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); * * WIP: very naive prime sieve; could be optimized using segmented ranges */ -static uint16 * +static uint32 * generate_primes(int limit) { /* upper bound of number of primes below limit */ @@ -300,7 +300,7 @@ generate_primes(int limit) int numprimes = 1.26 * limit / log(limit); bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); - uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + uint32 *primes = (uint32 *) palloc0(numprimes * sizeof(uint32)); int maxfactor = floor(sqrt(limit)); int factor = 2; /* first prime */ @@ -344,13 +344,18 @@ set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) int pidx = 0; int sum = 0; int target_partlen = nbits / nhashes; + uint32 *primes; + + elog(LOG, "generating primes nbits %u nhashes %u target_partlen %d", nbits, nhashes, target_partlen); /* * Increase the limit to ensure we have some primes higher than * the target partition length. The 100 value is arbitrary, but * should be well over what we need. */ - uint16 *primes = generate_primes(target_partlen + 100); + primes = generate_primes(target_partlen + 100); + + elog(LOG, "primes generated"); /* * In our array of primes, find a sequence of length nhashes, whose @@ -358,12 +363,14 @@ set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) * array will be filled with zeros, so we need to guard against that. */ while (primes[pidx + nhashes - 1] <= target_partlen && - primes[pidx] > 0) + primes[pidx + nhashes] > 0) pidx++; for (int i = 0; i < nhashes; i++) sum += primes[pidx + i]; + elog(LOG, "nbits %d sum %d", nbits, sum); + /* * Since all the primes are less than or equal the desired partition * length, the sum is somewhat less than nbits. Increment the starting @@ -383,6 +390,8 @@ set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) pidx++; } + elog(LOG, "nbits %d sum %d", nbits, sum); + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); /* WIP: assuming it's not important to pfree primes */ -- 2.26.2
0007-BRIN-minmax-multi-indexes-20201107.patch (text/x-patch, 217.3K) download From 2922c955903458bc49e4d8f7c4812e1949bdfc73 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 15:26:11 +0100 Subject: [PATCH 7/8] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2389 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4792 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index fea13791d6..660769ef5e 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -590,6 +590,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..f9a7ae6608 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2389 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_MINMAX_MULTI_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 6774f597a4..91833f12c5 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -159,6 +159,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + /* * Now obtain the values of each stored datum. Note that some values * might be toasted, and we cannot rely on the original heap values @@ -498,6 +506,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -577,6 +590,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 3c0df5597f..64ae71a58d 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1884,6 +1884,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1961,6 +2107,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1987,6 +2150,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2054,6 +2233,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2085,6 +2330,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2107,6 +2369,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2129,6 +2408,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2193,6 +2489,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2344,6 +2657,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2399,6 +2858,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2421,6 +2897,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2477,6 +2970,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2499,6 +3009,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2565,6 +3092,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 15f3baea15..8d212ede8b 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -953,6 +953,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1048,6 +1194,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1094,6 +1257,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1144,6 +1324,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1197,6 +1451,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1231,6 +1505,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1264,6 +1558,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1349,6 +1663,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1474,6 +1807,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1544,6 +2041,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1578,6 +2095,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1638,6 +2175,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1669,7 +2226,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1724,6 +2302,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 7351ecb07e..7a059dfc74 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8143,6 +8143,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11040,3 +11105,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 59da6a9804..af8d010c69 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -628,3 +628,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 69c39a1ca6..5050980cd4 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 6388bc7843..aa8bd4437e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -109,6 +109,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.26.2
0008-Ignore-correlation-for-new-BRIN-opclasses-20201107.patch (text/x-patch, 4.2K) download From 3c1bcbb7f79bc651b9d3cbc752ae0c15dd0be730 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 8/8] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index f6e65c0157..4d0f01a3ea 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -795,6 +795,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index f9a7ae6608..4f6262f241 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index bec357fcef..d84bf4726c 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
bloom-test.sh (application/x-shellscript, 7.4K) download ^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2020-11-08 18:21                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2020-11-08 18:21 UTC (permalink / raw)
  To: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; +Cc: pgsql-hackers@lists.postgresql.org

Seems I forgot to replace uint16 with uint32 in couple places when
fixing the one-hash code, so it was triggering SIGFPE because of
division by 0. Here's a fixed patch series.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments: download all (8 files, .tar.gz)

0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20201108.patch (text/x-patch, 23.5K) download From 60240e7fdd1674f100cde863e4a97602bad79d52 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:04 +0200 Subject: [PATCH 1/8] Pass all scan keys to BRIN consistent function at once Passing all scan keys to the BRIN consistent function at once may allow elimination of additional ranges, which would be impossible when only passing individual scan keys. The code continues to support both the original (one scan key at a time) and new (all scan keys at once) approaches, depending on whether the consistent function accepts three or four arguments. This modifies the existing BRIN opclasses (minmax, inclusion) although those don't really benefit from this change. The primary purpose of this is to allow more advanced opclases in the future. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 150 +++++++++++++++----- src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++-------- src/backend/access/brin/brin_minmax.c | 121 +++++++++++----- src/backend/access/brin/brin_validate.c | 4 +- src/include/catalog/pg_proc.dat | 4 +- 5 files changed, 324 insertions(+), 125 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 1f72562c60..ccf609e799 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -389,6 +389,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; + ScanKey **keys; + int *nkeys; + int keyno; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -410,6 +413,61 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) */ consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); + /* + * Make room for per-attribute lists of scan keys that we'll pass to the + * consistent support procedure. + */ + keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + + /* + * Preprocess the scan keys - split them into per-attribute arrays. + */ + for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + { + ScanKey key = &scan->keyData[keyno]; + AttrNumber keyattno = key->sk_attno; + + /* + * The collation of the scan key must match the collation + * used in the index column (but only if the search is not + * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using + * this index ... + */ + Assert((key->sk_flags & SK_ISNULL) || + (key->sk_collation == + TupleDescAttr(bdesc->bd_tupdesc, + keyattno - 1)->attcollation)); + + /* First time we see this index attribute, so init as needed. */ + if (!keys[keyattno-1]) + { + FmgrInfo *tmp; + + /* + * This is a bit of an overkill - we don't know how many + * scan keys are there for this attribute, so we simply + * allocate the largest number possible. This may waste + * a bit of memory, but we only expect small number of + * scan keys in general, so this should be negligible, + * and it's cheaper than having to repalloc repeatedly. + */ + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + /* First time this column, so look up consistent function */ + Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + + /* Add key to the per-attribute array. */ + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } + /* allocate an initial in-memory tuple, out of the per-range memcxt */ dtup = brin_new_memtuple(bdesc); @@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) } else { - int keyno; + int attno; /* * Compare scan keys with summary values stored for the range. @@ -480,51 +538,75 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * no keys. */ addrange = true; - for (keyno = 0; keyno < scan->numberOfKeys; keyno++) + for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { - ScanKey key = &scan->keyData[keyno]; - AttrNumber keyattno = key->sk_attno; - BrinValues *bval = &dtup->bt_columns[keyattno - 1]; + BrinValues *bval; Datum add; - /* - * The collation of the scan key must match the collation - * used in the index column (but only if the search is not - * IS NULL/ IS NOT NULL). Otherwise we shouldn't be using - * this index ... - */ - Assert((key->sk_flags & SK_ISNULL) || - (key->sk_collation == - TupleDescAttr(bdesc->bd_tupdesc, - keyattno - 1)->attcollation)); + /* skip attributes without any san keys */ + if (nkeys[attno - 1] == 0) + continue; - /* First time this column? look up consistent function */ - if (consistentFn[keyattno - 1].fn_oid == InvalidOid) - { - FmgrInfo *tmp; + bval = &dtup->bt_columns[attno - 1]; - tmp = index_getprocinfo(idxRel, keyattno, - BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyattno - 1], tmp, - CurrentMemoryContext); - } + Assert((nkeys[attno - 1] > 0) && + (nkeys[attno - 1] <= scan->numberOfKeys)); /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added * to the output bitmap. * - * When there are multiple scan keys, failure to meet the - * criteria for a single one of them is enough to discard - * the range as a whole, so break out of the loop as soon - * as a false return value is obtained. + * The opclass may or may not support processing of multiple + * scan keys. We can determine that based on the number of + * arguments - functions with extra parameter (number of scan + * keys) do support this, otherwise we have to simply pass the + * scan keys one by one, */ - add = FunctionCall3Coll(&consistentFn[keyattno - 1], - key->sk_collation, - PointerGetDatum(bdesc), - PointerGetDatum(bval), - PointerGetDatum(key)); - addrange = DatumGetBool(add); + if (consistentFn[attno - 1].fn_nargs >= 4) + { + Oid collation; + + /* + * Collation from the first key (has to be the same for + * all keys for the same attribue). + */ + collation = keys[attno - 1][0]->sk_collation; + + /* Check all keys at once */ + add = FunctionCall4Coll(&consistentFn[attno - 1], + collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1]), + Int32GetDatum(nkeys[attno - 1])); + addrange = DatumGetBool(add); + } + else + { + /* + * Check keys one by one + * + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + int keyno; + + for (keyno = 0; keyno < nkeys[attno - 1]; keyno++) + { + add = FunctionCall3Coll(&consistentFn[attno - 1], + keys[attno - 1][keyno]->sk_collation, + PointerGetDatum(bdesc), + PointerGetDatum(bval), + PointerGetDatum(keys[attno - 1][keyno])); + addrange = DatumGetBool(add); + if (!addrange) + break; + } + } + if (!addrange) break; } diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 7e380d66ed..853727190c 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum); static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); /* @@ -258,53 +260,109 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - Datum unionval; - AttrNumber attno; - Datum query; - FmgrInfo *finfo; - Datum result; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* Handle IS NULL/IS NOT NULL tests. */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); - } + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + PG_RETURN_BOOL(false); + } - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ + PG_RETURN_BOOL(false); + } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; } - /* If it is all nulls, it cannot possibly be consistent. */ - if (column->bv_allnulls) + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) PG_RETURN_BOOL(false); + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); + /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) PG_RETURN_BOOL(true); - attno = key->sk_attno; - subtype = key->sk_subtype; - query = key->sk_argument; - unionval = column->bv_values[INCLUSION_UNION]; + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. + */ + if (!inclusion_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(true); +} + +/* + * inclusion_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum query = key->sk_argument; + Datum unionval = column->bv_values[INCLUSION_UNION]; + Datum result; + switch (key->sk_strategy) { /* @@ -324,49 +382,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverLeftStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTRightStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverBelowStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTAboveStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTOverAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); case RTAboveStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTOverBelowStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); /* * Overlap and contains strategies @@ -385,7 +443,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, key->sk_strategy); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Contained by strategies @@ -406,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); /* * Adjacent strategy @@ -425,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTOverlapStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, - RTAdjacentStrategyNumber); + RTAdjacentStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_DATUM(result); + return DatumGetBool(result); /* * Basic comparison strategies @@ -460,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTRightStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTSameStrategyNumber: case RTEqualStrategyNumber: @@ -470,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) RTContainsStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterEqualStrategyNumber: finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); if (!DatumGetBool(result)) - PG_RETURN_BOOL(true); + return true; - PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]); case RTGreaterStrategyNumber: /* no need to check for empty elements */ finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, RTLeftStrategyNumber); result = FunctionCall2Coll(finfo, colloid, unionval, query); - PG_RETURN_BOOL(!DatumGetBool(result)); + return !DatumGetBool(result); default: /* shouldn't happen */ elog(ERROR, "invalid strategy number %d", key->sk_strategy); - PG_RETURN_BOOL(false); + return false; } } diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 4b5d6a7213..b233558310 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -30,6 +30,8 @@ typedef struct MinmaxOpaque static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); +static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, + ScanKey key, Oid colloid); Datum @@ -146,47 +148,104 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) { BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); - ScanKey key = (ScanKey) PG_GETARG_POINTER(2); - Oid colloid = PG_GET_COLLATION(), - subtype; - AttrNumber attno; - Datum value; - Datum matches; - FmgrInfo *finfo; - - Assert(key->sk_attno == column->bv_attno); + ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2); + int nkeys = PG_GETARG_INT32(3); + Oid colloid = PG_GET_COLLATION(); + int keyno; + bool regular_keys = false; - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) + /* + * First check if there are any IS NULL scan keys, and if we're + * violating them. In that case we can terminate early, without + * inspecting the ranges. + */ + for (keyno = 0; keyno < nkeys; keyno++) { - if (key->sk_flags & SK_SEARCHNULL) + ScanKey key = keys[keyno]; + + Assert(key->sk_attno == column->bv_attno); + + /* handle IS NULL/IS NOT NULL tests */ + if (key->sk_flags & SK_ISNULL) { - if (column->bv_allnulls || column->bv_hasnulls) - PG_RETURN_BOOL(true); + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + continue; /* this key is fine, continue */ + + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + if (key->sk_flags & SK_SEARCHNOTNULL) + { + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + continue; + } + + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and return false. + */ PG_RETURN_BOOL(false); } + else + /* note we have regular (non-NULL) scan keys */ + regular_keys = true; + } - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - PG_RETURN_BOOL(!column->bv_allnulls); + /* + * If the page range is all nulls, it cannot possibly be consistent if + * there are some regular scan keys. + */ + if (column->bv_allnulls && regular_keys) + PG_RETURN_BOOL(false); + + /* If there are no regular keys, the page range is considered consistent. */ + if (!regular_keys) + PG_RETURN_BOOL(true); - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. + /* Check that the range is consistent with all scan keys. */ + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* ignore IS NULL/IS NOT NULL tests handled above */ + if (key->sk_flags & SK_ISNULL) + continue; + + /* + * When there are multiple scan keys, failure to meet the + * criteria for a single one of them is enough to discard + * the range as a whole, so break out of the loop as soon + * as a false return value is obtained. */ - PG_RETURN_BOOL(false); + if (!minmax_consistent_key(bdesc, column, key, colloid)) + PG_RETURN_DATUM(false);; } - /* if the range is all empty, it cannot possibly be consistent */ - if (column->bv_allnulls) - PG_RETURN_BOOL(false); + PG_RETURN_DATUM(true); +} + +/* + * minmax_consistent_key + * Determine if the range is consistent with a single scan key. + */ +static bool +minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key, + Oid colloid) +{ + FmgrInfo *finfo; + AttrNumber attno = key->sk_attno; + Oid subtype = key->sk_subtype; + Datum value = key->sk_argument; + Datum matches; - attno = key->sk_attno; - subtype = key->sk_subtype; - value = key->sk_argument; switch (key->sk_strategy) { case BTLessStrategyNumber: @@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) break; } - PG_RETURN_DATUM(matches); + return DatumGetBool(matches); } /* diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index fb0615463e..0c28137c8f 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid) break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, - 3, 3, INTERNALOID, INTERNALOID, - INTERNALOID); + 3, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INT4OID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index c01da4bf01..bcbbc97de0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8137,7 +8137,7 @@ prosrc => 'brin_minmax_add_value' }, { oid => '3385', descr => 'BRIN minmax support', proname => 'brin_minmax_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_minmax_consistent' }, { oid => '3386', descr => 'BRIN minmax support', proname => 'brin_minmax_union', prorettype => 'bool', @@ -8153,7 +8153,7 @@ prosrc => 'brin_inclusion_add_value' }, { oid => '4107', descr => 'BRIN inclusion support', proname => 'brin_inclusion_consistent', prorettype => 'bool', - proargtypes => 'internal internal internal', + proargtypes => 'internal internal internal int4', prosrc => 'brin_inclusion_consistent' }, { oid => '4108', descr => 'BRIN inclusion support', proname => 'brin_inclusion_union', prorettype => 'bool', -- 2.26.2
0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20201108.patch (text/x-patch, 23.3K) download From e4f9a4c5a19a976552ff67472cc6504d7c5c3ed1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Thu, 17 Sep 2020 17:26:10 +0200 Subject: [PATCH 2/8] Move IS [NOT] NULL handling from BRIN support functions The handling of IS [NOT] NULL clauses is independent of an opclass, and most of the code was exactly the same in both minmax and inclusion. So instead move the code from support procedures to the AM methods etc. This simplifies the code quite a bit - especially the support procedures quite a bit, as they don't need to care about NULL values and flags at all. It also means the IS [NOT] NULL clauses can be evaluated without invoking the support procedure at all. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Mark Dilger <hornschnorter@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com> Reviewed-by: John Naylor <john.naylor@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 293 +++++++++++++++++------ src/backend/access/brin/brin_inclusion.c | 106 +------- src/backend/access/brin/brin_minmax.c | 103 +------- src/include/access/brin_internal.h | 3 + 4 files changed, 239 insertions(+), 266 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index ccf609e799..4dd29c7b10 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -35,6 +35,7 @@ #include "storage/freespace.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/index_selfuncs.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state); static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b); static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy); - +static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc, + BrinMemTuple *dtup, Datum *values, bool *nulls); +static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys); /* * BRIN handler function: return IndexAmRoutine with access method parameters @@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, OffsetNumber off; BrinTuple *brtup; BrinMemTuple *dtup; - int keyno; CHECK_FOR_INTERRUPTS(); @@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls, dtup = brin_deform_tuple(bdesc, brtup, NULL); - /* - * Compare the key values of the new tuple to the stored index values; - * our deformed tuple will get updated if the new tuple doesn't fit - * the original range (note this means we can't break out of the loop - * early). Make a note of whether this happens, so that we know to - * insert the modified tuple later. - */ - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - Datum result; - BrinValues *bval; - FmgrInfo *addValue; - - bval = &dtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - result = FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - /* if that returned true, we need to insert the updated tuple */ - need_insert |= DatumGetBool(result); - } + need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls); if (!need_insert) { @@ -389,8 +367,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinMemTuple *dtup; BrinTuple *btup = NULL; Size btupsz = 0; - ScanKey **keys; - int *nkeys; + ScanKey **keys, + **nullkeys; + int *nkeys, + *nnullkeys; int keyno; opaque = (BrinOpaque *) scan->opaque; @@ -415,10 +395,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* * Make room for per-attribute lists of scan keys that we'll pass to the - * consistent support procedure. + * consistent support procedure. We keep null and regular keys separate, + * so that we can easily pass regular keys to the consistent function. */ keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -439,23 +422,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) TupleDescAttr(bdesc->bd_tupdesc, keyattno - 1)->attcollation)); - /* First time we see this index attribute, so init as needed. */ - if (!keys[keyattno-1]) + /* + * First time we see this index attribute, so init as needed. + * + * This is a bit of an overkill - we don't know how many scan + * keys are there for a given attribute, so we simply allocate + * the largest number possible (as if all scan keys belonged to + * the same attribute). This may waste a bit of memory, but we + * only expect small number of scan keys in general, so this + * should be negligible, and it's probably cheaper than having + * to repalloc repeatedly. + */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) { FmgrInfo *tmp; - /* - * This is a bit of an overkill - we don't know how many - * scan keys are there for this attribute, so we simply - * allocate the largest number possible. This may waste - * a bit of memory, but we only expect small number of - * scan keys in general, so this should be negligible, - * and it's cheaper than having to repalloc repeatedly. - */ - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - - /* First time this column, so look up consistent function */ - Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid); + /* No key/null arrays for this attribute. */ + Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); + Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -463,9 +447,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CurrentMemoryContext); } - /* Add key to the per-attribute array. */ - keys[keyattno - 1][nkeys[keyattno - 1]] = key; - nkeys[keyattno - 1]++; + /* Add key to the proper per-attribute array. */ + if (key->sk_flags & SK_ISNULL) + { + if (!nullkeys[keyattno - 1]) + nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; + nnullkeys[keyattno - 1]++; + } + else + { + if (!keys[keyattno - 1]) + keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); + + keys[keyattno - 1][nkeys[keyattno - 1]] = key; + nkeys[keyattno - 1]++; + } } /* allocate an initial in-memory tuple, out of the per-range memcxt */ @@ -543,15 +541,57 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) BrinValues *bval; Datum add; - /* skip attributes without any san keys */ - if (nkeys[attno - 1] == 0) + /* + * skip attributes without any scan keys (both regular + * and IS [NOT] NULL) + */ + if (nkeys[attno - 1] == 0 && nnullkeys[attno - 1] == 0) continue; bval = &dtup->bt_columns[attno - 1]; + /* + * First check if there are any IS [NOT] NULL scan keys, + * and if we're violating them. In that case we can + * terminate early, without invoking the support function. + * + * As there may be more keys, we can only detemine mismatch + * within this loop. + */ + if (bdesc->bd_info[attno - 1]->oi_regular_nulls && + !check_null_keys(bval, nullkeys[attno - 1], + nnullkeys[attno - 1])) + { + /* + * If any of the IS [NOT] NULL keys failed, the page + * range as a whole can't pass. So terminate the loop. + */ + addrange = false; + break; + } + + /* + * So either there are no IS [NOT] NULL keys, or all passed. + * If there are no regular scan keys, we're done - the page + * range matches. If there are regular keys, but the page + * range is marked as 'all nulls' it can't possibly pass + * (we're assuming the operators are strict). + */ + + /* No regular scan keys - page range as a whole passes. */ + if (!nkeys[attno - 1]) + continue; + Assert((nkeys[attno - 1] > 0) && (nkeys[attno - 1] <= scan->numberOfKeys)); + /* If it is all nulls, it cannot possibly be consistent. */ + if (bval->bv_allnulls) + { + addrange = false; + break; + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added @@ -694,7 +734,6 @@ brinbuildCallback(Relation index, { BrinBuildState *state = (BrinBuildState *) brstate; BlockNumber thisblock; - int i; thisblock = ItemPointerGetBlockNumber(tid); @@ -723,25 +762,8 @@ brinbuildCallback(Relation index, } /* Accumulate the current tuple into the running state */ - for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++) - { - FmgrInfo *addValue; - BrinValues *col; - Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i); - - col = &state->bs_dtuple->bt_columns[i]; - addValue = index_getprocinfo(index, i + 1, - BRIN_PROCNUM_ADDVALUE); - - /* - * Update dtuple state, if and as necessary. - */ - FunctionCall4Coll(addValue, - attr->attcollation, - PointerGetDatum(state->bs_bdesc), - PointerGetDatum(col), - values[i], isnull[i]); - } + (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple, + values, isnull); } /* @@ -1520,6 +1542,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b) FmgrInfo *unionFn; BrinValues *col_a = &a->bt_columns[keyno]; BrinValues *col_b = &db->bt_columns[keyno]; + BrinOpcInfo *opcinfo = bdesc->bd_info[keyno]; + + if (opcinfo->oi_regular_nulls) + { + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + continue; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the + * values from B into A, and we're done. We cannot run the + * operators in this case, because values in A might contain + * garbage. Note we already established that B contains values. + */ + if (col_a->bv_allnulls) + { + int i; + + col_a->bv_allnulls = false; + + for (i = 0; i < opcinfo->oi_nstored; i++) + col_a->bv_values[i] = + datumCopy(col_b->bv_values[i], + opcinfo->oi_typcache[i]->typbyval, + opcinfo->oi_typcache[i]->typlen); + + continue; + } + } unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1, BRIN_PROCNUM_UNION); @@ -1573,3 +1628,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy) */ FreeSpaceMapVacuum(idxrel); } + +static bool +add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup, + Datum *values, bool *nulls) +{ + int keyno; + bool modified = false; + + /* + * Compare the key values of the new tuple to the stored index values; + * our deformed tuple will get updated if the new tuple doesn't fit + * the original range (note this means we can't break out of the loop + * early). Make a note of whether this happens, so that we know to + * insert the modified tuple later. + */ + for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) + { + Datum result; + BrinValues *bval; + FmgrInfo *addValue; + + bval = &dtup->bt_columns[keyno]; + + if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno]) + { + /* + * If the new value is null, we record that we saw it if it's + * the first one; otherwise, there's nothing to do. + */ + if (!bval->bv_hasnulls) + { + bval->bv_hasnulls = true; + modified = true; + } + + continue; + } + + addValue = index_getprocinfo(idxRel, keyno + 1, + BRIN_PROCNUM_ADDVALUE); + result = FunctionCall4Coll(addValue, + idxRel->rd_indcollation[keyno], + PointerGetDatum(bdesc), + PointerGetDatum(bval), + values[keyno], + nulls[keyno]); + /* if that returned true, we need to insert the updated tuple */ + modified |= DatumGetBool(result); + } + + return modified; +} + +static bool +check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys) +{ + int keyno; + + /* + * First check if there are any IS [NOT] NULL scan keys, and if we're + * violating them. + */ + for (keyno = 0; keyno < nnullkeys; keyno++) + { + ScanKey key = nullkeys[keyno]; + + Assert(key->sk_attno == bval->bv_attno); + + /* Handle only IS NULL/IS NOT NULL tests */ + if (!(key->sk_flags & SK_ISNULL)) + continue; + + if (key->sk_flags & SK_SEARCHNULL) + { + /* IS NULL scan key, but range has no NULLs */ + if (!bval->bv_allnulls && !bval->bv_hasnulls) + return false; + } + else if (key->sk_flags & SK_SEARCHNOTNULL) + { + /* + * For IS NOT NULL, we can only skip ranges that are known to + * have only nulls. + */ + if (bval->bv_allnulls) + return false; + } + else + { + /* + * Neither IS NULL nor IS NOT NULL was used; assume all indexable + * operators are strict and thus return false with NULL value in + * the scan key. + */ + return false; + } + } + + return true; +} diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 853727190c..f5eaef416f 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS) */ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); result->oi_nstored = 3; + result->oi_regular_nulls = true; result->oi_opaque = (InclusionOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); @@ -141,7 +142,7 @@ brin_inclusion_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_GETARG_BOOL(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *finfo; Datum result; @@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) AttrNumber attno; Form_pg_attribute attr; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* It has to be checked, if it contains elements that are not mergeable. */ if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) @@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Datum result; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls". */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do. */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[INCLUSION_UNION] = - datumCopy(col_b->bv_values[INCLUSION_UNION], - attr->attbyval, attr->attlen); - col_a->bv_values[INCLUSION_UNMERGEABLE] = - col_b->bv_values[INCLUSION_UNMERGEABLE]; - col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = - col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; - PG_RETURN_VOID(); - } - /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index b233558310..8886ad984e 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(MinmaxOpaque)); result->oi_nstored = 2; + result->oi_regular_nulls = true; result->oi_opaque = (MinmaxOpaque *) MAXALIGN((char *) result + SizeofBrinOpcInfo(2)); result->oi_typcache[0] = result->oi_typcache[1] = @@ -69,7 +70,7 @@ brin_minmax_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_GETARG_DATUM(3); + bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3); Oid colloid = PG_GET_COLLATION(); FmgrInfo *cmpFn; Datum compar; @@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS) Form_pg_attribute attr; AttrNumber attno; - /* - * If the new value is null, we record that we saw it if it's the first - * one; otherwise, there's nothing to do. - */ - if (isnull) - { - if (column->bv_hasnulls) - PG_RETURN_BOOL(false); - - column->bv_hasnulls = true; - PG_RETURN_BOOL(true); - } + Assert(!isnull); attno = column->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); @@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS) int nkeys = PG_GETARG_INT32(3); Oid colloid = PG_GET_COLLATION(); int keyno; - bool regular_keys = false; - - /* - * First check if there are any IS NULL scan keys, and if we're - * violating them. In that case we can terminate early, without - * inspecting the ranges. - */ - for (keyno = 0; keyno < nkeys; keyno++) - { - ScanKey key = keys[keyno]; - - Assert(key->sk_attno == column->bv_attno); - - /* handle IS NULL/IS NOT NULL tests */ - if (key->sk_flags & SK_ISNULL) - { - if (key->sk_flags & SK_SEARCHNULL) - { - if (column->bv_allnulls || column->bv_hasnulls) - continue; /* this key is fine, continue */ - - PG_RETURN_BOOL(false); - } - - /* - * For IS NOT NULL, we can only skip ranges that are known to have - * only nulls. - */ - if (key->sk_flags & SK_SEARCHNOTNULL) - { - if (column->bv_allnulls) - PG_RETURN_BOOL(false); - - continue; - } - - /* - * Neither IS NULL nor IS NOT NULL was used; assume all indexable - * operators are strict and return false. - */ - PG_RETURN_BOOL(false); - } - else - /* note we have regular (non-NULL) scan keys */ - regular_keys = true; - } - - /* - * If the page range is all nulls, it cannot possibly be consistent if - * there are some regular scan keys. - */ - if (column->bv_allnulls && regular_keys) - PG_RETURN_BOOL(false); - - /* If there are no regular keys, the page range is considered consistent. */ - if (!regular_keys) - PG_RETURN_BOOL(true); /* Check that the range is consistent with all scan keys. */ for (keyno = 0; keyno < nkeys; keyno++) { ScanKey key = keys[keyno]; - /* ignore IS NULL/IS NOT NULL tests handled above */ - if (key->sk_flags & SK_ISNULL) - continue; + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); /* * When there are multiple scan keys, failure to meet the @@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS) bool needsadj; Assert(col_a->bv_attno == col_b->bv_attno); - - /* Adjust "hasnulls" */ - if (!col_a->bv_hasnulls && col_b->bv_hasnulls) - col_a->bv_hasnulls = true; - - /* If there are no values in B, there's nothing left to do */ - if (col_b->bv_allnulls) - PG_RETURN_VOID(); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); - /* - * Adjust "allnulls". If A doesn't have values, just copy the values from - * B into A, and we're done. We cannot run the operators in this case, - * because values in A might contain garbage. Note we already established - * that B contains values. - */ - if (col_a->bv_allnulls) - { - col_a->bv_allnulls = false; - col_a->bv_values[0] = datumCopy(col_b->bv_values[0], - attr->attbyval, attr->attlen); - col_a->bv_values[1] = datumCopy(col_b->bv_values[1], - attr->attbyval, attr->attlen); - PG_RETURN_VOID(); - } - /* Adjust minimum, if B's min is less than A's min */ finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid, BTLessStrategyNumber); diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 9ffc9100c0..7c4f3da0a0 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -27,6 +27,9 @@ typedef struct BrinOpcInfo /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; + /* Regular processing of NULLs in BrinValues? */ + bool oi_regular_nulls; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
0003-Optimize-allocations-in-bringetbitmap-20201108.patch (text/x-patch, 4.4K) download From 17a3fc30c21ea02cf5bc87b303336ea1a9b529e0 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tv@fuzzy.cz> Date: Sun, 13 Sep 2020 12:12:47 +0200 Subject: [PATCH 3/8] Optimize allocations in bringetbitmap The bringetbitmap function allocates memory for various purposes, which may be quite expensive, depending on the number of scan keys. Instead of allocating them separately, allocate one bit chunk of memory an carve it into smaller pieces as needed - all the pieces have the same lifespan, and it saves quite a bit of CPU and memory overhead. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin.c | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 4dd29c7b10..470431010d 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -372,6 +372,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) int *nkeys, *nnullkeys; int keyno; + char *ptr; + Size len; + char *tmp PG_USED_FOR_ASSERTS_ONLY; opaque = (BrinOpaque *) scan->opaque; bdesc = opaque->bo_bdesc; @@ -397,11 +400,50 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) * Make room for per-attribute lists of scan keys that we'll pass to the * consistent support procedure. We keep null and regular keys separate, * so that we can easily pass regular keys to the consistent function. + * + * To reduce the allocation overhead, we allocate one big chunk and then + * carve it into smaller arrays ourselves. All the pieces have exactly + * the same lifetime, so that's OK. */ - keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); - nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); - nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts); + len = + /* regular keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts) + + /* NULL keys */ + MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys * bdesc->bd_tupdesc->natts) + + MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + ptr = palloc(len); + tmp = ptr; + + keys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nullkeys = (ScanKey **) ptr; + ptr += MAXALIGN(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts); + + nkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + nnullkeys = (int *) ptr; + ptr += MAXALIGN(sizeof(int) * bdesc->bd_tupdesc->natts); + + for (int i = 0; i < bdesc->bd_tupdesc->natts; i++) + { + keys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + + nullkeys[i] = (ScanKey *) ptr; + ptr += MAXALIGN(sizeof(ScanKey) * scan->numberOfKeys); + } + + Assert(tmp + len == ptr); + + /* zero the number of keys */ + memset(nkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); + memset(nnullkeys, 0, sizeof(int) * bdesc->bd_tupdesc->natts); /* * Preprocess the scan keys - split them into per-attribute arrays. @@ -437,9 +479,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) { FmgrInfo *tmp; - /* No key/null arrays for this attribute. */ - Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0)); - Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0)); + /* First time we see this attribute, so no key/null keys. */ + Assert(nkeys[keyattno - 1] == 0); + Assert(nnullkeys[keyattno - 1] == 0); tmp = index_getprocinfo(idxRel, keyattno, BRIN_PROCNUM_CONSISTENT); @@ -450,17 +492,11 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm) /* Add key to the proper per-attribute array. */ if (key->sk_flags & SK_ISNULL) { - if (!nullkeys[keyattno - 1]) - nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key; nnullkeys[keyattno - 1]++; } else { - if (!keys[keyattno - 1]) - keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys); - keys[keyattno - 1][nkeys[keyattno - 1]] = key; nkeys[keyattno - 1]++; } -- 2.26.2
0004-BRIN-bloom-indexes-20201108.patch (text/x-patch, 138.9K) download From 634698bf07f5e8aaeb8ad925e95cea049f089e5d Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 15:21:18 +0100 Subject: [PATCH 4/8] BRIN bloom indexes Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN indexes using the new opclasses only allow equality queries, but that works for data like UUID, MAC addresses etc. for which range queries are not very common (and the data look random, making BRIN minmax indexes inefficient anyway). BRIN bloom opclasses allow specifying the usual Bloom parameters, like expected number of distinct values (in the BRIN range) and desired false positive rate. The opclasses store 32-bit hashes of the values, not the raw indexed values. This assumes the hash functions for data types has low number of collisions, good performance etc. Collisions are not a huge issue though, because the number of values in a BRIN ranges is fairly small. The summary does not start as a Bloom filter right away - it initially stores a plain array of hashes. Only when the size of the array grows too large it switches to proper Bloom filter. This means that ranges with low number of distinct values the summary will use less space. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 178 ++++ doc/src/sgml/ref/create_index.sgml | 31 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_bloom.c | 1112 +++++++++++++++++++++ src/include/access/brin.h | 2 + src/include/access/brin_internal.h | 4 + src/include/catalog/pg_amop.dat | 170 ++++ src/include/catalog/pg_amproc.dat | 447 +++++++++ src/include/catalog/pg_opclass.dat | 72 ++ src/include/catalog/pg_opfamily.dat | 38 + src/include/catalog/pg_proc.dat | 34 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_bloom.out | 456 +++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/expected/psql.out | 3 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 5 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_bloom.sql | 404 ++++++++ 19 files changed, 2970 insertions(+), 5 deletions(-) create mode 100644 src/backend/access/brin/brin_bloom.c create mode 100644 src/test/regress/expected/brin_bloom.out create mode 100644 src/test/regress/sql/brin_bloom.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4420794e5b..577dd18c49 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was </row> </thead> <tbody> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bit,bit)</literal></entry> + </row> <row> <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry> <entry><literal>= (bit,bit)</literal></entry> @@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row> <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row> + <row> + <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry> + <entry><literal>= (character,character)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry> <entry><literal>= (character,character)</literal></entry> @@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (character,character)</literal></entry></row> <row><entry><literal>&gt;= (character,character)</literal></entry></row> + <row> + <entry valign="middle"><literal>bytea_bloom_ops</literal></entry> + <entry><literal>= (bytea,bytea)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry> <entry><literal>= (bytea,bytea)</literal></entry> @@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row> <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row> + <row> + <entry valign="middle"><literal>char_bloom_ops</literal></entry> + <entry><literal>= ("char","char")</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry> <entry><literal>= ("char","char")</literal></entry> @@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; ("char","char")</literal></entry></row> <row><entry><literal>&gt;= ("char","char")</literal></entry></row> + <row> + <entry valign="middle"><literal>date_bloom_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry> <entry><literal>= (date,date)</literal></entry> @@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle"><literal>float4_bloom_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle"><literal>float8_bloom_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>= (inet,inet)</literal></entry></row> <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>inet_bloom_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry> <entry><literal>= (inet,inet)</literal></entry> @@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle"><literal>int2_bloom_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle"><literal>int4_bloom_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle"><literal>int8_bloom_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle"><literal>interval_bloom_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle"><literal>name_bloom_ops</literal></entry> + <entry><literal>= (name,name)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (name,name)</literal></entry></row> <row><entry><literal>&gt;= (name,name)</literal></entry></row> + <row> + <entry valign="middle"><literal>numeric_bloom_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry> <entry><literal>= (numeric,numeric)</literal></entry> @@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle"><literal>oid_bloom_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row> <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row> + <row> + <entry valign="middle"><literal>text_bloom_ops</literal></entry> + <entry><literal>= (text,text)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry> <entry><literal>= (text,text)</literal></entry> @@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (text,text)</literal></entry></row> <row><entry><literal>&gt;= (text,text)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry> <entry><literal>= (tid,tid)</literal></entry> @@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle"><literal>time_bloom_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row> <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -779,6 +903,60 @@ typedef struct BrinOpcInfo function can improve index performance. </para> + <para> + To write an operator class for a data type that implements only an equality + operator and supports hashing, it is possible to use the bloom support procedures + alongside the corresponding operators, as shown in + <xref linkend="brin-extensibility-bloom-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-bloom-table"> + <title>Procedure and Support Numbers for Bloom Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_bloom_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_bloom_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_bloom_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_bloom_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute hash of an element</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator equal-to</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Support procedure numbers 1-10 are reserved for the BRIN internal + functions, so the SQL level functions start with number 11. Support + function number 11 is the main function required to build the index. + It should accept one argument with the same data type as the operator class, + and return a hash of the value. + </para> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 749db2845e..fea13791d6 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -559,6 +559,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>n_distinct_per_range</literal></term> + <listitem> + <para> + Defines the estimated number of distinct non-null values in the block + range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the + Bloom filter. It behaves similarly to <literal>n_distinct</literal> option + for <xref linkend="sql-altertable"/>. When set to a positive value, + each block range is assumed to contain this number of distinct non-null + values. When set to a negative value, which must be greater than or + equal to -1, the number of distinct non-null is assumed linear with + the maximum possible number of tuples in the block range (about 290 + rows per block). The default values is <literal>-0.1</literal>, and + the minimum number of distinct non-null values is <literal>128</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>false_positive_rate</literal></term> + <listitem> + <para> + Defines the desired false positive rate used by <acronym>BRIN</acronym> + bloom indexes for sizing of the Bloom filter. The values must be be + greater than 0.0 and smaller than 1.0. The default values is 0.01, + which is 1% false positive rate. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 468e1e289a..6b56131215 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ brin.o \ + brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ brin_pageops.o \ diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c new file mode 100644 index 0000000000..52b3d477c8 --- /dev/null +++ b/src/backend/access/brin/brin_bloom.c @@ -0,0 +1,1112 @@ +/* + * brin_bloom.c + * Implementation of Bloom opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * A BRIN opclass summarizing page range into a bloom filter. + * + * Bloom filters allow efficient test whether a given page range contains + * a particular value. Therefore, if we summarize each page range into a + * bloom filter, we can easily and cheaply test wheter it containst values + * we get later. + * + * The index only supports equality operator, similarly to hash indexes. + * BRIN bloom indexes are however much smaller, and support only bitmap + * scans. + * + * Note: Don't confuse this with bloom indexes, implemented in a contrib + * module. That extension implements an entirely new AM, building a bloom + * filter on multiple columns in a single row. This opclass works with an + * existing AM (BRIN) and builds bloom filter on a column. + * + * + * values vs. hashes + * ----------------- + * + * The original column values are not used directly, but are first hashed + * using the regular type-specific hash function, producing a uint32 hash. + * And this hash value is then added to the summary - either stored as is + * (in the sorted mode), or hashed again and added to the bloom filter. + * + * This allows the code to treat all data types (byval/byref/...) the same + * way, with only minimal space requirements. For example we don't need to + * store varlena types differently in the sorted mode, etc. Everything is + * uint32 making it much simpler. + * + * Of course, this assumes the built-in hash function is reasonably good, + * without too many collisions etc. But that does seem to be the case, at + * least based on past experience. After all, the same hash functions are + * used for hash indexes, hash partitioning and so on. + * + * + * sizing the bloom filter + * ----------------------- + * + * Size of a bloom filter depends on the number of distinct values we will + * store in it, and the desired false positive rate. The higher the number + * of distinct values and/or the lower the false positive rate, the larger + * the bloom filter. On the other hand, we want to keep the index as small + * as possible - that's one of the basic advantages of BRIN indexes. + * + * The number of distinct elements (in a page range) depends on the data, + * we can consider it fixed. This simplifies the trade-off to just false + * positive rate vs. size. + * + * At the page range level, false positive rate is a probability the bloom + * filter matches a random value. For the whole index (with sufficiently + * many page ranges) it represents the fraction of the index ranges (and + * thus fraction of the table to be scanned) matching the random value. + * + * Furthermore, the size of the bloom filter is subject to implementation + * limits - it has to fit onto a single index page (8kB by default). As + * the bitmap is inherently random, compression can't reliably help here. + * To reduce the size of a filter (to fit to a page), we have to either + * accept higher false positive rate (undesirable), or reduce the number + * of distinct items to be stored in the filter. We can't quite the input + * data, of course, but we may make the BRIN page ranges smaller - instead + * of the default 128 pages (1MB) we may build index with 16-page ranges, + * or something like that. This does help even for random data sets, as + * the number of rows per heap page is limited (to ~290 with very narrow + * tables, likely ~20 in practice). + * + * Of course, good sizing decisions depend on having the necessary data, + * i.e. number of distinct values in a page range (of a given size) and + * table size (to estimate cost change due to change in false positive + * rate due to having larger index vs. scanning larger indexes). We may + * not have that data - for example when building an index on empty table + * it's not really possible. And for some data we only have estimates for + * the whole table and we can only estimate per-range values (ndistinct). + * + * Another challenge is that while the bloom filter is per-column, it's + * the whole index tuple that has to fit into a page. And for multi-column + * indexes that may include pieces we have no control over (not necessarily + * bloom filters, the other columns may use other BRIN opclasses). So it's + * not entirely clear how to distrubute the space between those columns. + * + * The current logic, implemented in brin_bloom_get_ndistinct, attempts to + * make some basic sizing decisions, based on the table ndistinct estimate. + * + * + * sort vs. hash + * ------------- + * + * As explained in the preceding section, sizing bloom filters correctly is + * difficult in practice. It's also true that bloom filters quickly degrade + * after exceeding the expected number of distinct items. It's therefore + * expected that people will overshoot the parameters a bit (particularly + * the ndistinct per page range), perhaps by a factor of 2 or more. + * + * It's also possible the data set is not uniform - it may have ranges with + * very many distinct items per range, but also ranges with only very few + * distinct items. The bloom filter has to be sized for the more variable + * ranges, making it rather wasteful in the less variable part. + * + * For example, if some page ranges have 1000 distinct values, that means + * about ~1.2kB bloom filter with 1% false positive rate. For page ranges + * that only contain 10 distinct values, that's wasteful, because we might + * store the values (the uint32 hashes) in just 40B. + * + * To address these issues, the opclass stores the raw values directly, and + * only switches to the actual bloom filter after reaching the same space + * requirements. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_bloom.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +#include <math.h> + +#define BloomEqualStrategyNumber 1 + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_HASH 11 /* required */ + +/* + * Subtract this from procnum to obtain index in BloomOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/* + * Flags. We only use a single bit for now, to decide whether we're in the + * sorted or hash phase. So we have 15 bits for future use, if needed. The + * filters are expected to be hundreds of bytes, so this is negligible. + */ +#define BLOOM_FLAG_PHASE_HASH 0x0001 + +#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH) +#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f)) + +/* + * Number of hashes to accumulate before deduplicating and sorting in the + * sort phase? We want this fairly small to reduce the amount of space and + * also speed-up bsearch lookups. + */ +#define BLOOM_MAX_UNSORTED 32 + +/* + * Storage type for BRIN's reloptions. + */ +typedef struct BloomOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ +} BloomOptions; + +/* + * The current min value (16) is somewhat arbitrary, but it's based + * on the fact that the filter header is ~20B alone, which is about + * the same as the filter bitmap for 16 distinct items with 1% false + * positive rate. So by allowing lower values we'd not gain much. In + * any case, the min should not be larger than MaxHeapTuplesPerPage + * (~290), which is the theoretical maximum for single-page ranges. + */ +#define BLOOM_MIN_NDISTINCT_PER_RANGE 16 + +/* + * Used to determine number of distinct items, based on the number of rows + * in a page range. The 10% is somewhat similar to what estimate_num_groups + * does, so we use the same factor here. + */ +#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */ + +/* + * Allowed range and default value for the false positive range. The exact + * values are somewhat arbitrary. + */ +#define BLOOM_MIN_FALSE_POSITIVE_RATE 0.001 /* 0.1% fp rate */ +#define BLOOM_MAX_FALSE_POSITIVE_RATE 0.1 /* 10% fp rate */ +#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ + +#define BloomGetNDistinctPerRange(opts) \ + ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ + (((BloomOptions *) (opts))->nDistinctPerRange) : \ + BLOOM_DEFAULT_NDISTINCT_PER_RANGE) + +#define BloomGetFalsePositiveRate(opts) \ + ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \ + (((BloomOptions *) (opts))->falsePositiveRate) : \ + BLOOM_DEFAULT_FALSE_POSITIVE_RATE) + +/* + * Bloom Filter + * + * Represents a bloom filter, built on hashes of the indexed values. That is, + * we compute a uint32 hash of the value, and then store this hash into the + * bloom filter (and compute additional hashes on it). + * + * We use an optimisation that initially we store the uint32 values directly, + * without the extra hashing step. And only later filling the bitmap space, + * we switch to the regular bloom filter mode. + * + * PHASE_SORTED + * + * Initially we copy the uint32 hash into the bitmap, regularly sorting the + * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted + * values). + * + * The idea is that if we only see very few distinct values, we can store + * them in less space compared to the (sparse) bloom filter bitmap. It also + * stores them exactly, although that's not a big advantage as almost-empty + * bloom filter has false positive rate close to zero anyway. + * + * PHASE_HASH + * + * Once we fill the bitmap space in the sorted phase, we switch to the hash + * phase, where we actually use the bloom filter. We treat the uint32 hashes + * as input values, and hash them again with different seeds (to get the k + * hash functions needed for bloom filter). + * + * + * XXX Perhaps we could save a few bytes by using different data types, but + * considering the size of the bitmap, the difference is negligible. + * + * XXX We could also implement "sparse" bloom filters, keeping only the + * bytes that are not entirely 0. That might make the "sorted" phase + * mostly unnecessary. + * + * XXX We can also watch the number of bits set in the bloom filter, and + * then stop using it (and not store the bitmap, to save space) when the + * false positive rate gets too high. + */ +typedef struct BloomFilter +{ + /* varlena header (do not touch directly!) */ + int32 vl_len_; + + /* space for various flags (phase, etc.) */ + uint16 flags; + + /* fields used only in the SORTED phase */ + uint16 nvalues; /* number of hashes stored (total) */ + uint16 nsorted; /* number of hashes in the sorted part */ + + /* fields for the HASHED phase */ + uint8 nhashes; /* number of hash functions */ + uint32 nbits; /* number of bits in the bitmap (size) */ + uint32 nbits_set; /* number of bits set to 1 */ + + /* data of the bloom filter (used both for sorted and hashed phase) */ + char data[FLEXIBLE_ARRAY_MEMBER]; + +} BloomFilter; + +static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); + + +/* + * bloom_init + * Initialize the Bloom Filter, allocate all the memory. + * + * The filter is initialized with optimal size for ndistinct expected + * values, and requested false positive rate. The filter is stored as + * varlena. + */ +static BloomFilter * +bloom_init(int ndistinct, double false_positive_rate) +{ + Size len; + BloomFilter *filter; + + int m; /* number of bits */ + double k; /* number of hash functions */ + + Assert(ndistinct > 0); + Assert((false_positive_rate > 0) && (false_positive_rate < 1.0)); + + m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); + + /* round m to whole bytes */ + m = ((m + 7) / 8) * 8; + + /* + * round(log(2.0) * m / ndistinct), but assume round() may not be + * available on Windows + */ + k = log(2.0) * m / ndistinct; + k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + + /* + * Allocate the bloom filter with a minimum size 64B (about 40B in the + * bitmap part). We require space at least for the header. + * + * XXX Maybe the 64B min size is not really needed? + */ + len = Max(offsetof(BloomFilter, data), 64); + + filter = (BloomFilter *) palloc0(len); + + filter->flags = 0; /* implies SORTED phase */ + filter->nhashes = (int) k; + filter->nbits = m; + + SET_VARSIZE(filter, len); + + return filter; +} + +/* simple uint32 comparator, for pg_qsort and bsearch */ +static int +cmp_uint32(const void *a, const void *b) +{ + uint32 *ia = (uint32 *) a; + uint32 *ib = (uint32 *) b; + + if (*ia == *ib) + return 0; + else if (*ia < *ib) + return -1; + else + return 1; +} + +/* + * bloom_compact + * Compact the filter during the 'sorted' phase. + * + * We sort the uint32 hashes and remove duplicates, for two main reasons. + * Firstly, to keep most of the data sorted for bsearch lookups. Secondly, + * we try to save space by removing the duplicates, allowing us to stay + * in the sorted phase a bit longer. + * + * We currently don't repalloc the bitmap, i.e. we don't free the memory + * here - in the worst case we waste space for up to 32 unsorted hashes + * (if all of them are already in the sorted part), so about 128B. We can + * either reduce the number of unsorted items (e.g. to 8 hashes, which + * would mean 32B), or start doing the repalloc. + * + * We do however set the varlena length, to minimize the storage needs. + */ +static void +bloom_compact(BloomFilter *filter) +{ + int i, j, k; + Size len; + + uint32 *values; + uint32 *result; + uint32 *sorted; + uint32 *unsorted; + + int nvalues; + int nsorted; + int nunsorted; + + /* never call compact on filters in HASH phase */ + Assert(BLOOM_IS_SORTED(filter)); + + /* when already fully sorted, no chance to compact anything */ + if (filter->nvalues == filter->nsorted) + return; + + values = (uint32 *) filter->data; + + /* result of merging */ + k = 0; + result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues); + + /* first we have sorted data, then unsorted */ + sorted = values; + nsorted = filter->nsorted; + + unsorted = &values[filter->nsorted]; + nunsorted = filter->nvalues - filter->nsorted; + + /* sort the unsorted part, then merge the two parts */ + pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32); + + i = 0; /* sorted index */ + j = 0; /* unsorted index */ + + while ((i < nsorted) && (j < nunsorted)) + { + if (sorted[i] <= unsorted[j]) + result[k++] = sorted[i++]; + else + result[k++] = unsorted[j++]; + } + + while (i < nsorted) + result[k++] = sorted[i++]; + + while (j < nunsorted) + result[k++] = unsorted[j++]; + + /* compact - remove duplicate values */ + nvalues = 1; + for (i = 1; i < filter->nvalues; i++) + { + /* if different from the last value, keep it */ + if (result[i] != result[nvalues - 1]) + result[nvalues++] = result[i]; + } + + memcpy(filter->data, result, sizeof(uint32) * nvalues); + + filter->nvalues = nvalues; + filter->nsorted = nvalues; + + len = offsetof(BloomFilter, data) + + (filter->nvalues) * sizeof(uint32); + + SET_VARSIZE(filter, len); +} + +/* + * bloom_add_value + * Add value to the bloom filter. + */ +static BloomFilter * +bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) +{ + int i; + uint32 big_h, h, d; + + /* assume 'not updated' by default */ + Assert(filter); + + /* if we're in the sorted phase, we store the hashes directly */ + if (BLOOM_IS_SORTED(filter)) + { + /* how many uint32 hashes can we fit into the bitmap */ + int maxvalues = filter->nbits / (8 * sizeof(uint32)); + + /* do not overflow the bitmap space or number of unsorted items */ + Assert(filter->nvalues <= maxvalues); + Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED); + + /* + * In this branch we always update the filter - we either add the + * hash to the unsorted part, or switch the filter to hashing. + */ + if (updated) + *updated = true; + + /* + * If the array is full, or if we reached the limit on unsorted + * items, try to compact the filter first, before attempting to + * add the new value. + */ + if ((filter->nvalues == maxvalues) || + (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED)) + bloom_compact(filter); + + /* + * Can we squeeze one more uint32 hash into the bitmap? Also make + * sure there's enough space in the bytea value first. + */ + if (filter->nvalues < maxvalues) + { + Size len = VARSIZE_ANY(filter); + Size need = offsetof(BloomFilter, data) + + (filter->nvalues + 1) * sizeof(uint32); + + /* + * We don't double the size here, as in the first place we care about + * reducing storage requirements, and the doubling happens automatically + * in memory contexts (so the repalloc should be cheap in most cases). + */ + if (len < need) + { + filter = (BloomFilter *) repalloc(filter, need); + SET_VARSIZE(filter, need); + } + + /* copy the new value into the filter */ + memcpy(&filter->data[filter->nvalues * sizeof(uint32)], + &value, sizeof(uint32)); + + filter->nvalues++; + + /* we're done */ + return filter; + } + + /* can't add any more exact hashes, so switch to hashing */ + filter = bloom_switch_to_hashing(filter); + } + + /* we better be in the hashing phase */ + Assert(BLOOM_IS_HASHED(filter)); + + /* compute the hashes, used for the bloom filter */ + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, set it and remember we did that */ + if (! (filter->data[byte] & (0x01 << bit))) + { + filter->data[byte] |= (0x01 << bit); + filter->nbits_set++; + if (updated) + *updated = true; + } + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + return filter; +} + +/* + * bloom_switch_to_hashing + * Switch the bloom filter from sorted to hashing mode. + */ +static BloomFilter * +bloom_switch_to_hashing(BloomFilter *filter) +{ + int i; + uint32 *values; + Size len; + BloomFilter *newfilter; + + Assert(filter->nbits % 8 == 0); + + /* + * The new filter is allocated with all the memory, directly into + * the HASH phase. + */ + len = offsetof(BloomFilter, data) + (filter->nbits / 8); + + newfilter = (BloomFilter *) palloc0(len); + + newfilter->nhashes = filter->nhashes; + newfilter->nbits = filter->nbits; + newfilter->flags |= BLOOM_FLAG_PHASE_HASH; + + SET_VARSIZE(newfilter, len); + + values = (uint32 *) filter->data; + + for (i = 0; i < filter->nvalues; i++) + /* ignore the return value here, re don't repalloc in hashing mode */ + bloom_add_value(newfilter, values[i], NULL); + + /* free the original filter, return the newly allocated one */ + pfree(filter); + + return newfilter; +} + +/* + * bloom_contains_value + * Check if the bloom filter contains a particular value. + */ +static bool +bloom_contains_value(BloomFilter *filter, uint32 value) +{ + int i; + uint32 big_h, h, d; + + Assert(filter); + + /* in sorted mode we simply search the two arrays (sorted, unsorted) */ + if (BLOOM_IS_SORTED(filter)) + { + int i; + uint32 *values = (uint32 *) filter->data; + + /* first search through the sorted part */ + if ((filter->nsorted > 0) && + (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL)) + return true; + + /* now search through the unsorted part - linear search */ + for (i = filter->nsorted; i < filter->nvalues; i++) + { + if (value == values[i]) + return true; + } + + /* nothing found */ + return false; + } + + /* now the regular hashing mode */ + Assert(BLOOM_IS_HASHED(filter)); + + big_h = ((uint32) DatumGetInt64(hash_uint32(value))); + + h = big_h % filter->nbits; + d = big_h % (filter->nbits - 1); + + /* compute the requested number of hashes */ + for (i = 0; i < filter->nhashes; i++) + { + int byte = (h / 8); + int bit = (h % 8); + + /* if the bit is not set, the value is not there */ + if (! (filter->data[byte] & (0x01 << bit))) + return false; + + /* next bit */ + h += d++; + if (h >= filter->nbits) + h -= filter->nbits; + + if (d == filter->nbits) + d = 0; + } + + /* all hashes found in bloom filter */ + return true; +} + +typedef struct BloomOpaque +{ + /* + * XXX At this point we only need a single proc (to compute the hash), + * but let's keep the array just like inclusion and minman opclasses, + * for consistency. We may need additional procs in the future. + */ + FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS]; + bool extra_proc_missing[BLOOM_MAX_PROCNUMS]; +} BloomOpaque; + +static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + + +Datum +brin_bloom_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + * + * bloom indexes only store the filter as a single BYTEA column + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(BloomOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (BloomOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_BLOOM_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * brin_bloom_get_ndistinct + * Determine the ndistinct value used to size bloom filter. + * + * Tweak the ndistinct value based on the pagesPerRange value. First, + * if it's negative, it's assumed to be relative to maximum number of + * tuples in the range (assuming each page gets MaxHeapTuplesPerPage + * tuples, which is likely a significant over-estimate). We also clamp + * the value, not to over-size the bloom filter unnecessarily. + * + * XXX We can only do this when the pagesPerRange value was supplied. + * If it wasn't, it has to be a read-only access to the index, in which + * case we don't really care. But perhaps we should fall-back to the + * default pagesPerRange value? + * + * XXX We might also fetch info about ndistinct estimate for the column, + * and compute the expected number of distinct values in a range. But + * that may be tricky due to data being sorted in various ways, so it + * seems better to rely on the upper estimate. + */ +static int +brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts) +{ + double ndistinct; + double maxtuples; + BlockNumber pagesPerRange; + + pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index); + ndistinct = BloomGetNDistinctPerRange(opts); + + Assert(BlockNumberIsValid(pagesPerRange)); + + maxtuples = MaxHeapTuplesPerPage * pagesPerRange; + + /* + * Similarly to n_distinct, negative values are relative - in this + * case to maximum number of tuples in the page range (maxtuples). + */ + if (ndistinct < 0) + ndistinct = (-ndistinct) * maxtuples; + + /* + * Positive values are to be used directly, but we still apply a + * couple of safeties no to use unreasonably small bloom filters. + */ + ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE); + + /* + * And don't use more than the maximum possible number of tuples, + * in the range, which would be entirely wasteful. + */ + ndistinct = Min(ndistinct, maxtuples); + + return (int) ndistinct; +} + +static double +brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts) +{ + return BloomGetFalsePositiveRate(opts); +} + +/* + * Examine the given index tuple (which contains 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 bloom filter 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_bloom_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); + BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *hashFn; + uint32 hashValue; + bool updated = false; + AttrNumber attno; + BloomFilter *filter; + + Assert(!isnull); + + attno = column->bv_attno; + + /* + * If this is the first non-null value, we need to initialize the bloom + * filter. Otherwise just extract the existing bloom filter from BrinValues. + */ + if (column->bv_allnulls) + { + filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts), + brin_bloom_get_fp_rate(bdesc, opts)); + column->bv_values[0] = PointerGetDatum(filter); + column->bv_allnulls = false; + updated = true; + } + else + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + /* + * Compute the hash of the new value, using the supplied hash function, + * and then add the hash value to the bloom filter. + */ + hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval)); + + filter = bloom_add_value(filter, hashValue, &updated); + + column->bv_values[0] = PointerGetDatum(filter); + + PG_RETURN_BOOL(updated); +} + +/* + * 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 bloom + * filter. Return true if so, false otherwise. + */ +Datum +brin_bloom_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(); + AttrNumber attno; + Datum value; + Datum matches; + FmgrInfo *finfo; + uint32 hashValue; + BloomFilter *filter; + int keyno; + + filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]); + + Assert(filter); + + matches = true; + + for (keyno = 0; keyno < nkeys; keyno++) + { + ScanKey key = keys[keyno]; + + /* NULL keys are handled and filtered-out in bringetbitmap */ + Assert(!(key->sk_flags & SK_ISNULL)); + + attno = key->sk_attno; + value = key->sk_argument; + + switch (key->sk_strategy) + { + case BloomEqualStrategyNumber: + + /* + * In the equality case (WHERE col = someval), we want to return + * the current page range if the minimum value in the range <= + * scan key, and the maximum value >= scan key. + */ + finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH); + + hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value)); + matches &= bloom_contains_value(filter, hashValue); + + break; + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + if (!matches) + break; + } + + PG_RETURN_DATUM(matches); +} + +/* + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + * + * XXX We assume the bloom filters have the same parameters fow now. In the + * future we should have 'can union' function, to decide if we can combine + * two particular bloom filters. + */ +Datum +brin_bloom_union(PG_FUNCTION_ARGS) +{ + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + BloomFilter *filter_a; + BloomFilter *filter_b; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]); + filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + /* make sure neither of the bloom filters is NULL */ + Assert(filter_a && filter_b); + Assert(filter_a->nbits == filter_b->nbits); + + /* + * Merging of the filters depends on the phase of both filters. + */ + if (BLOOM_IS_SORTED(filter_b)) + { + /* + * Simply read all items from 'b' and add them to 'a' (the phase of + * 'a' does not really matter). + */ + int i; + uint32 *values = (uint32 *) filter_b->data; + + for (i = 0; i < filter_b->nvalues; i++) + filter_a = bloom_add_value(filter_a, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_a); + } + else if (BLOOM_IS_SORTED(filter_a)) + { + /* + * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values + * from 'a' into the new copy. + */ + int i; + BloomFilter *filter_c; + uint32 *values = (uint32 *) filter_a->data; + + filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1)); + + for (i = 0; i < filter_a->nvalues; i++) + filter_c = bloom_add_value(filter_c, values[i], NULL); + + col_a->bv_values[0] = PointerGetDatum(filter_c); + } + else if (BLOOM_IS_HASHED(filter_a)) + { + /* + * 'b' hashed, 'a' hashed - merge the bitmaps by OR + */ + int i; + int nbytes = (filter_a->nbits + 7) / 8; + + /* we better have "compatible" bloom filters */ + Assert(filter_a->nbits == filter_b->nbits); + Assert(filter_a->nhashes == filter_b->nhashes); + + for (i = 0; i < nbytes; i++) + filter_a->data[i] |= filter_b->data[i]; + } + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + BloomOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +Datum +brin_bloom_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(BloomOptions)); + + add_local_real_reloption(relopts, "n_distinct_per_range", + "number of distinct items expected in a BRIN page range", + BLOOM_DEFAULT_NDISTINCT_PER_RANGE, + -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange)); + + add_local_real_reloption(relopts, "false_positive_rate", + "desired false-positive rate for the bloom filters", + BLOOM_DEFAULT_FALSE_POSITIVE_RATE, + BLOOM_MIN_FALSE_POSITIVE_RATE, + BLOOM_MAX_FALSE_POSITIVE_RATE, + offsetof(BloomOptions, falsePositiveRate)); + + PG_RETURN_VOID(); +} + +/* + * brin_bloom_summary_in + * - input routine for type brin_bloom_summary. + * + * brin_bloom_summary is only used internally to represent summaries + * in BRIN bloom indexes, so it has no operations of its own, and we + * disallow input too. + */ +Datum +brin_bloom_summary_in(PG_FUNCTION_ARGS) +{ + /* + * brin_bloom_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", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* + * brin_bloom_summary_out + * - output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized into a bytea value, but we want + * to output something nicer humans can understand. + */ +Datum +brin_bloom_summary_out(PG_FUNCTION_ARGS) +{ + BloomFilter *filter; + StringInfoData str; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0)); + + if (BLOOM_IS_HASHED(filter)) + { + appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + filter->nhashes, filter->nbits, filter->nbits_set); + } + else + { + appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u", + filter->nvalues, filter->nsorted); + /* TODO include the sorted/unsorted values */ + } + + appendStringInfoChar(&str, '}'); + + PG_RETURN_CSTRING(str.data); +} + +/* + * brin_bloom_summary_recv + * - binary input routine for type brin_bloom_summary. + */ +Datum +brin_bloom_summary_recv(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * brin_bloom_summary_send + * - binary output routine for type brin_bloom_summary. + * + * BRIN bloom summaries are serialized in a bytea value (although the + * type is named differently), so let's just send that. + */ +Datum +brin_bloom_summary_send(PG_FUNCTION_ARGS) +{ + return byteasend(fcinfo); +} diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 0987878dd2..b02298c0aa 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -22,6 +22,8 @@ typedef struct BrinOptions int32 vl_len_; /* varlena header (do not touch directly!) */ BlockNumber pagesPerRange; bool autosummarize; + double nDistinctPerRange; /* number of distinct values per range */ + double falsePositiveRate; /* false positive for bloom filter */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 7c4f3da0a0..e3c9d47503 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -58,6 +58,10 @@ typedef struct BrinDesc /* total number of Datum entries that are stored on-disk for all columns */ int bd_totalstored; + /* parameters for sizing bloom filter (BRIN bloom opclasses) */ + double bd_nDistinctPerRange; + double bd_falsePositiveRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index bbe357fbc0..3c0df5597f 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1689,6 +1689,11 @@ amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)', amopmethod => 'brin' }, +# bloom bytea +{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea', + amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)', + amopmethod => 'brin' }, + # minmax "char" { amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char', amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)', @@ -1706,6 +1711,11 @@ amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)', amopmethod => 'brin' }, +# bloom "char" +{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char', + amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)', + amopmethod => 'brin' }, + # minmax name { amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name', amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)', @@ -1723,6 +1733,11 @@ amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)', amopmethod => 'brin' }, +# bloom name +{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name', + amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)', + amopmethod => 'brin' }, + # minmax integer { amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8', @@ -1869,6 +1884,44 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# bloom integer + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, + # minmax text { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text', amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)', @@ -1886,6 +1939,11 @@ amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)', amopmethod => 'brin' }, +# bloom text +{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text', + amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)', + amopmethod => 'brin' }, + # minmax oid { amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', @@ -1903,6 +1961,11 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# bloom oid +{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, + # minmax tid { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', @@ -1920,6 +1983,11 @@ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', amopmethod => 'brin' }, +# tid oid +{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, + # minmax float (float4, float8) { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4', @@ -1986,6 +2054,20 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# bloom float +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, + # minmax macaddr { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2003,6 +2085,11 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# bloom macaddr +{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, + # minmax macaddr8 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2020,6 +2107,11 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# bloom macaddr8 +{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, + # minmax inet { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', @@ -2037,6 +2129,11 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# bloom inet +{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, + # inclusion inet { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)', @@ -2074,6 +2171,11 @@ amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)', amopmethod => 'brin' }, +# bloom character +{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar', + amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)', + amopmethod => 'brin' }, + # minmax time without time zone { amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', @@ -2091,6 +2193,11 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# bloom time without time zone +{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', + amopmethod => 'brin' }, + # minmax datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp', @@ -2237,6 +2344,44 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# bloom datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, + # minmax interval { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2254,6 +2399,11 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# bloom interval +{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, + # minmax time with time zone { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', @@ -2271,6 +2421,11 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# bloom time with time zone +{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, + # minmax bit { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit', amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)', @@ -2322,6 +2477,11 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# bloom numeric +{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, + # minmax uuid { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', @@ -2339,6 +2499,11 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# bloom uuid +{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, + # inclusion range types { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange', amoprighttype => 'anyrange', amopstrategy => '1', @@ -2400,6 +2565,11 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# bloom pg_lsn +{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # inclusion box { amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box', amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index a8e0c4ff8a..15f3baea15 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -772,6 +772,24 @@ { amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea', amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom bytea +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea', + amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' }, + # minmax "char" { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '1', @@ -785,6 +803,24 @@ { amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char', amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom "char" +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char', + amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' }, + # minmax name { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '1', @@ -798,6 +834,24 @@ { amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name', amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom name +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name', + amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' }, + # minmax integer: int2, int4, int8 { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -899,6 +953,58 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' }, + +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' }, + # minmax text { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '1', @@ -912,6 +1018,24 @@ { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text', amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom text +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text', + amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' }, + # minmax oid { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -924,6 +1048,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom oid +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' }, + # minmax tid { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -936,6 +1077,23 @@ { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom tid +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -986,6 +1144,45 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom float +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'hashfloat4' }, + +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'hashfloat8' }, + # minmax macaddr { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1000,6 +1197,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'hashmacaddr' }, + # minmax macaddr8 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1014,6 +1231,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom macaddr8 +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'hashmacaddr8' }, + # minmax inet { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1027,6 +1264,24 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom inet +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' }, + # inclusion inet { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1061,6 +1316,26 @@ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom character +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar', + amprocrighttype => 'bpchar', amprocnum => '11', + amproc => 'hashchar' }, + # minmax time without time zone { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1074,6 +1349,24 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time without time zone +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' }, + # minmax datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1181,6 +1474,62 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'timestamp_hash' }, + +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' }, + # minmax interval { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1195,6 +1544,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom interval +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'interval_hash' }, + # minmax time with time zone { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1209,6 +1578,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom time with time zone +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'timetz_hash' }, + # minmax bit { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit', amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' }, @@ -1249,6 +1638,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom numeric +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'hash_numeric' }, + # minmax uuid { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '1', @@ -1262,6 +1671,24 @@ { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom uuid +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' }, + # inclusion range types { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange', amprocrighttype => 'anyrange', amprocnum => '1', @@ -1297,6 +1724,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# bloom pg_lsn +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_bloom_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_bloom_add_value' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_bloom_consistent' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_bloom_union' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_bloom_options' }, +{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'pg_lsn_hash' }, + # inclusion box { amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index f2342bb328..ca747a03b9 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -257,67 +257,130 @@ { opcmethod => 'brin', opcname => 'bytea_minmax_ops', opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea', opckeytype => 'bytea' }, +{ opcmethod => 'brin', opcname => 'bytea_bloom_ops', + opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea', + opckeytype => 'bytea', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'char_minmax_ops', opcfamily => 'brin/char_minmax_ops', opcintype => 'char', opckeytype => 'char' }, +{ opcmethod => 'brin', opcname => 'char_bloom_ops', + opcfamily => 'brin/char_bloom_ops', opcintype => 'char', + opckeytype => 'char', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'name_minmax_ops', opcfamily => 'brin/name_minmax_ops', opcintype => 'name', opckeytype => 'name' }, +{ opcmethod => 'brin', opcname => 'name_bloom_ops', + opcfamily => 'brin/name_bloom_ops', opcintype => 'name', + opckeytype => 'name', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_bloom_ops', + opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'text_minmax_ops', opcfamily => 'brin/text_minmax_ops', opcintype => 'text', opckeytype => 'text' }, +{ opcmethod => 'brin', opcname => 'text_bloom_ops', + opcfamily => 'brin/text_bloom_ops', opcintype => 'text', + opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_bloom_ops', + opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'tid_minmax_ops', opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' }, +{ opcmethod => 'brin', opcname => 'tid_bloom_ops', + opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', + opcdefault => 'f'}, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_bloom_ops', + opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops', + opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', + opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_bloom_ops', + opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_inclusion_ops', opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet', opckeytype => 'inet' }, { opcmethod => 'brin', opcname => 'bpchar_minmax_ops', opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar', opckeytype => 'bpchar' }, +{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops', + opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar', + opckeytype => 'bpchar', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_bloom_ops', + opcfamily => 'brin/time_bloom_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', + opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_bloom_ops', + opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_bloom_ops', + opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'bit_minmax_ops', opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' }, { opcmethod => 'brin', opcname => 'varbit_minmax_ops', @@ -326,18 +389,27 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_bloom_ops', + opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, # no brin opclass for record, anyarray { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_bloom_ops', + opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'range_inclusion_ops', opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange', opckeytype => 'anyrange' }, { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', + opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, # no brin opclass for enum, tsvector, tsquery, jsonb diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index cf0fb325b3..8875079698 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,50 +180,88 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '8100', + opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, +{ oid => '8101', + opfmethod => 'brin', opfname => 'text_bloom_ops' }, +{ oid => '8102', + opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '8103', + opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '8104', + opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', opfmethod => 'brin', opfname => 'char_minmax_ops' }, +{ oid => '8105', + opfmethod => 'brin', opfname => 'char_bloom_ops' }, { oid => '4064', opfmethod => 'brin', opfname => 'bytea_minmax_ops' }, +{ oid => '8106', + opfmethod => 'brin', opfname => 'bytea_bloom_ops' }, { oid => '4065', opfmethod => 'brin', opfname => 'name_minmax_ops' }, +{ oid => '8107', + opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '8108', + opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, +{ oid => '8123', + opfmethod => 'brin', opfname => 'tid_bloom_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '8109', + opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '8110', + opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '8111', + opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, +{ oid => '8112', + opfmethod => 'brin', opfname => 'network_bloom_ops' }, { oid => '4076', opfmethod => 'brin', opfname => 'bpchar_minmax_ops' }, +{ oid => '8113', + opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '8114', + opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '8115', + opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', opfmethod => 'brin', opfname => 'bit_minmax_ops' }, { oid => '4080', opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '8116', + opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '8117', + opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', opfmethod => 'brin', opfname => 'box_inclusion_ops' }, { oid => '5000', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bcbbc97de0..7351ecb07e 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8160,6 +8160,26 @@ proargtypes => 'internal internal internal', prosrc => 'brin_inclusion_union' }, +# BRIN bloom +{ oid => '8118', descr => 'BRIN bloom support', + proname => 'brin_bloom_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' }, +{ oid => '8119', descr => 'BRIN bloom support', + proname => 'brin_bloom_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_bloom_add_value' }, +{ oid => '8120', descr => 'BRIN bloom support', + proname => 'brin_bloom_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_bloom_consistent' }, +{ oid => '8121', descr => 'BRIN bloom support', + proname => 'brin_bloom_union', prorettype => 'bool', + proargtypes => 'internal internal internal', + prosrc => 'brin_bloom_union' }, +{ oid => '8122', descr => 'BRIN bloom support', + proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_bloom_options' }, + # userlock replacements { oid => '2880', descr => 'obtain exclusive advisory lock', proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r', @@ -11006,3 +11026,17 @@ prosrc => 'unicode_is_normalized' }, ] + +{ oid => '9035', descr => 'I/O', + proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary', + proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' }, +{ oid => '9036', descr => 'I/O', + proname => 'brin_bloom_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' }, +{ oid => '9037', descr => 'I/O', + proname => 'brin_bloom_summary_recv', provolatile => 's', + prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal', + prosrc => 'brin_bloom_summary_recv' }, +{ oid => '9038', descr => 'I/O', + proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 21a467a7a7..59da6a9804 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -621,3 +621,10 @@ typalign => 'd', typstorage => 'x' }, ] + +{ oid => '9034', + descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out new file mode 100644 index 0000000000..19b866283a --- /dev/null +++ b/src/test/regress/expected/brin_bloom.out @@ -0,0 +1,456 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +ERROR: value -1.1 out of bounds for option "n_distinct_per_range" +DETAIL: Valid values are between "-1.000000" and "2147483647.000000". +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0009) +); +ERROR: value 0.0009 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "0.100000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.11) +); +ERROR: value 0.11 out of bounds for option "false_positive_rate" +DETAIL: Valid values are between "0.001000" and "0.100000". +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_bloom; -- force a summarization cycle in brinidx +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +ERROR: "brintest_bloom" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_bloom + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_bloom_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_bloom + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 7ed29b4961..9b5715da86 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2018,6 +2018,7 @@ ORDER BY 1, 2, 3; 2742 | 16 | @@ 3580 | 1 | < 3580 | 1 | << + 3580 | 1 | = 3580 | 2 | &< 3580 | 2 | <= 3580 | 3 | && @@ -2081,7 +2082,7 @@ ORDER BY 1, 2, 3; 4000 | 26 | >> 4000 | 27 | >>= 4000 | 28 | ^@ -(123 rows) +(124 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index daac0ff49d..72c7598c89 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4989,8 +4989,9 @@ List of access methods List of operator classes AM | Input type | Storage type | Operator class | Default? ------+------------+--------------+----------------+---------- + brin | oid | | oid_bloom_ops | no brin | oid | | oid_minmax_ops | yes -(1 row) +(2 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 274130e706..97bf9797de 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------- + oid | typname +------+----------------------- 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list -(4 rows) + 9034 | pg_brin_bloom_summary +(5 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index ae89ed7f0b..69c39a1ca6 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash +# ---------- +# Additional BRIN tests +# ---------- +test: brin_bloom + # ---------- # Another group of parallel tests # ---------- diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 525bdc804f..6388bc7843 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -108,6 +108,7 @@ test: delete test: namespace test: prepared_xacts test: brin +test: brin_bloom test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql new file mode 100644 index 0000000000..3c2ef56316 --- /dev/null +++ b/src/test/regress/sql/brin_bloom.sql @@ -0,0 +1,404 @@ +CREATE TABLE brintest_bloom (byteacol bytea, + charcol "char", + namecol name, + int8col bigint, + int2col smallint, + int4col integer, + textcol text, + oidcol oid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + bpcharcol character, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 8)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 8), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test bloom specific index options +-- ndistinct must be >= -1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(n_distinct_per_range = -1.1) +); +-- false_positive_rate must be between 0.001 and 1.0 +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.0009) +); +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops(false_positive_rate = 0.11) +); + +CREATE INDEX brinidx_bloom ON brintest_bloom USING brin ( + byteacol bytea_bloom_ops, + charcol char_bloom_ops, + namecol name_bloom_ops, + int8col int8_bloom_ops, + int2col int2_bloom_ops, + int4col int4_bloom_ops, + textcol text_bloom_ops, + oidcol oid_bloom_ops, + float4col float4_bloom_ops, + float8col float8_bloom_ops, + macaddrcol macaddr_bloom_ops, + inetcol inet_bloom_ops, + cidrcol inet_bloom_ops, + bpcharcol bpchar_bloom_ops, + datecol date_bloom_ops, + timecol time_bloom_ops, + timestampcol timestamp_bloom_ops, + timestamptzcol timestamptz_bloom_ops, + intervalcol interval_bloom_ops, + timetzcol timetz_bloom_ops, + numericcol numeric_bloom_ops, + uuidcol uuid_bloom_ops, + lsncol pg_lsn_bloom_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_bloom (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_bloom VALUES + ('byteacol', 'bytea', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('charcol', '"char"', + '{=}', + '{M}', + '{6}'), + ('namecol', 'name', + '{=}', + '{MAAAAA}', + '{2}'), + ('int2col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int2col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int2', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int4', + '{=}', + '{800}', + '{1}'), + ('int4col', 'int8', + '{=}', + '{800}', + '{1}'), + ('int8col', 'int8', + '{=}', + '{1257141600}', + '{1}'), + ('textcol', 'text', + '{=}', + '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}', + '{1}'), + ('oidcol', 'oid', + '{=}', + '{8800}', + '{1}'), + ('float4col', 'float4', + '{=}', + '{1}', + '{4}'), + ('float4col', 'float8', + '{=}', + '{1}', + '{4}'), + ('float8col', 'float4', + '{=}', + '{0}', + '{1}'), + ('float8col', 'float8', + '{=}', + '{0}', + '{1}'), + ('macaddrcol', 'macaddr', + '{=}', + '{2c:00:2d:00:16:00}', + '{2}'), + ('inetcol', 'inet', + '{=}', + '{10.2.14.231/24}', + '{1}'), + ('inetcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'inet', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'inet', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('cidrcol', 'cidr', + '{=}', + '{10.2.14/24}', + '{2}'), + ('cidrcol', 'cidr', + '{=}', + '{fe80::6e40:8ff:fea9:8c46}', + '{1}'), + ('bpcharcol', 'bpchar', + '{=}', + '{W}', + '{6}'), + ('datecol', 'date', + '{=}', + '{2009-12-01}', + '{1}'), + ('timecol', 'time', + '{=}', + '{02:28:57}', + '{1}'), + ('timestampcol', 'timestamp', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestampcol', 'timestamptz', + '{=}', + '{1964-03-24 19:26:45}', + '{1}'), + ('timestamptzcol', 'timestamptz', + '{=}', + '{1972-10-19 09:00:00-07}', + '{1}'), + ('intervalcol', 'interval', + '{=}', + '{1 mons 13 days 12:24}', + '{1}'), + ('timetzcol', 'timetz', + '{=}', + '{01:35:50+02}', + '{2}'), + ('numericcol', 'numeric', + '{=}', + '{2268164.347826086956521739130434782609}', + '{1}'), + ('uuidcol', 'uuid', + '{=}', + '{52225222-5222-5222-5222-522252225222}', + '{1}'), + ('lsncol', 'pg_lsn', + '{=, IS, IS NOT}', + '{44/455222, NULL, NULL}', + '{1, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_bloom SELECT + repeat(stringu1, 42)::bytea, + substr(stringu1, 1, 1)::"char", + stringu1::name, 142857 * tenthous, + thousand, + twothousand, + repeat(stringu1, 42), + unique1::oid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + substr(stringu1, 1, 1)::bpchar, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_bloom', 0); +VACUUM brintest_bloom; -- force a summarization cycle in brinidx + +UPDATE brintest_bloom SET int8col = int8col * int4col; +UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 0); +SELECT brin_desummarize_range('brinidx_bloom', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_bloom ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_bloom_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_bloom_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_bloom_idx', -1); +SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_bloom (a INT, b INT); +INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_bloom; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1; -- 2.26.2
0005-use-one-hash-bloom-variant-20201108.patch (text/x-patch, 8.5K) download From 566e9f01f86d702ca83b4152f4d0859c1a9ee9f1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Mon, 2 Nov 2020 23:55:28 +0100 Subject: [PATCH 5/8] use one-hash bloom variant --- src/backend/access/brin/brin_bloom.c | 176 +++++++++++++++++++++------ 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 52b3d477c8..73f8478b4c 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -206,6 +206,9 @@ typedef struct BloomOptions #define BLOOM_MAX_FALSE_POSITIVE_RATE 0.1 /* 10% fp rate */ #define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */ +/* With the minimum allowed false positive rate of 0.001, we need up to 10 hashes */ +#define BLOOM_MAX_NUM_PARTITIONS 10 + #define BloomGetNDistinctPerRange(opts) \ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \ (((BloomOptions *) (opts))->nDistinctPerRange) : \ @@ -273,6 +276,7 @@ typedef struct BloomFilter uint8 nhashes; /* number of hash functions */ uint32 nbits; /* number of bits in the bitmap (size) */ uint32 nbits_set; /* number of bits set to 1 */ + uint16 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ /* data of the bloom filter (used both for sorted and hashed phase) */ char data[FLEXIBLE_ARRAY_MEMBER]; @@ -282,6 +286,114 @@ typedef struct BloomFilter static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); +/* + * generate_primes + * returns array of all primes less than limit + * + * WIP: very naive prime sieve; could be optimized using segmented ranges + */ +static uint16 * +generate_primes(int limit) +{ + /* upper bound of number of primes below limit */ + /* WIP: reference for this number */ + int numprimes = 1.26 * limit / log(limit); + + bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); + uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + + int maxfactor = floor(sqrt(limit)); + int factor = 2; /* first prime */ + + /* mark the sieve where the index is composite */ + while (factor < maxfactor) + { + for (int i = factor * factor; i < limit; i += factor) + is_composite[i] = true; + do { factor++; } while (is_composite[factor]); + } + + /* the unmarked numbers are prime, so copy over */ + for (int i = 2, j = 0; i < limit && j < numprimes; i++) + { + if (!is_composite[i]) + primes[j++] = i; + } + + /* there should still be some zeroes at the end, but make sure */ + primes[numprimes - 1] = 0; + + /* pretty large, so free it now (segmented ranges would make it smaller) */ + pfree(is_composite); + return primes; +} + +/* + * set_bloom_partitions + * Calculate k moduli for one-hashing bloom filter. + * + * Find consecutive primes whose sum is close to nbits and + * return the sum. Copy the primes to the filter to use as + * partition lengths. + * WIP: one-hashing bf paper ref somewhere + */ +static uint32 +set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) +{ + int min, diff, incr; + int pidx = 0; + int sum = 0; + int target_partlen = nbits / nhashes; + + /* + * Increase the limit to ensure we have some primes higher than + * the target partition length. The 100 value is arbitrary, but + * should be well over what we need. + */ + uint16 *primes = generate_primes(target_partlen + 100); + + /* + * In our array of primes, find a sequence of length nhashes, whose + * last item is close to our target partition length. The end of the + * array will be filled with zeros, so we need to guard against that. + */ + while (primes[pidx + nhashes - 1] <= target_partlen && + primes[pidx] > 0) + pidx++; + + for (int i = 0; i < nhashes; i++) + sum += primes[pidx + i]; + + /* + * Since all the primes are less than or equal the desired partition + * length, the sum is somewhat less than nbits. Increment the starting + * point until we find the sequence of primes whose sum is closest to + * nbits. It doesn't matter whether it's higher or lower. + */ + min = abs(nbits - sum); + for (;;) + { + incr = primes[pidx + nhashes] - primes[pidx]; + diff = abs(nbits - (sum + incr)); + if (diff >= min) + break; + + min = diff; + sum += incr; + pidx++; + } + + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); + + /* WIP: assuming it's not important to pfree primes */ + + /* + * The actual filter length will be the sum of the partition lengths + * rounded up to the nearest byte. + */ + return (uint32) ((sum + 7) / 8) * 8; +} + /* * bloom_init * Initialize the Bloom Filter, allocate all the memory. @@ -304,15 +416,13 @@ bloom_init(int ndistinct, double false_positive_rate) m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0))))); - /* round m to whole bytes */ - m = ((m + 7) / 8) * 8; - /* * round(log(2.0) * m / ndistinct), but assume round() may not be * available on Windows */ k = log(2.0) * m / ndistinct; k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k); + k = Min(k, BLOOM_MAX_NUM_PARTITIONS); /* * Allocate the bloom filter with a minimum size 64B (about 40B in the @@ -326,7 +436,9 @@ bloom_init(int ndistinct, double false_positive_rate) filter->flags = 0; /* implies SORTED phase */ filter->nhashes = (int) k; - filter->nbits = m; + + /* calculate the partition lengths and adjust m to match */ + filter->nbits = set_bloom_partitions(m, k, filter->partlens); SET_VARSIZE(filter, len); @@ -448,7 +560,7 @@ static BloomFilter * bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) { int i; - uint32 big_h, h, d; + int part_boundary = 0; /* assume 'not updated' by default */ Assert(filter); @@ -517,17 +629,16 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) /* we better be in the hashing phase */ Assert(BLOOM_IS_HASHED(filter)); - /* compute the hashes, used for the bloom filter */ - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); + + Assert(bitloc < filter->nbits); /* if the bit is not set, set it and remember we did that */ if (! (filter->data[byte] & (0x01 << bit))) @@ -539,12 +650,7 @@ bloom_add_value(BloomFilter *filter, uint32 value, bool *updated) } /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } return filter; @@ -574,6 +680,7 @@ bloom_switch_to_hashing(BloomFilter *filter) newfilter->nhashes = filter->nhashes; newfilter->nbits = filter->nbits; + memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint16)); newfilter->flags |= BLOOM_FLAG_PHASE_HASH; SET_VARSIZE(newfilter, len); @@ -598,7 +705,7 @@ static bool bloom_contains_value(BloomFilter *filter, uint32 value) { int i; - uint32 big_h, h, d; + int part_boundary = 0; Assert(filter); @@ -627,28 +734,23 @@ bloom_contains_value(BloomFilter *filter, uint32 value) /* now the regular hashing mode */ Assert(BLOOM_IS_HASHED(filter)); - big_h = ((uint32) DatumGetInt64(hash_uint32(value))); - - h = big_h % filter->nbits; - d = big_h % (filter->nbits - 1); - /* compute the requested number of hashes */ for (i = 0; i < filter->nhashes; i++) { - int byte = (h / 8); - int bit = (h % 8); + int partlen = filter->partlens[i]; + int bitloc = part_boundary + (value % partlen); + + int byte = (bitloc / 8); + int bit = (bitloc % 8); + + Assert(bitloc < filter->nbits); /* if the bit is not set, the value is not there */ if (! (filter->data[byte] & (0x01 << bit))) return false; /* next bit */ - h += d++; - if (h >= filter->nbits) - h -= filter->nbits; - - if (d == filter->nbits) - d = 0; + part_boundary += partlen; } /* all hashes found in bloom filter */ @@ -1069,8 +1171,14 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS) if (BLOOM_IS_HASHED(filter)) { - appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u", + appendStringInfo(&str, + "mode: hashed nhashes: %u nbits: %u nbits_set: %u partition lengths: [", filter->nhashes, filter->nbits, filter->nbits_set); + for (int i = 0; i < filter->nhashes - 1; i++) + { + appendStringInfo(&str, "%u, ", filter->partlens[i]); + } + appendStringInfo(&str, "%u]", filter->partlens[filter->nhashes - 1]); } else { -- 2.26.2
0006-one-hash-tweaks-20201108.patch (text/x-patch, 3.7K) download From b9837275d0f4abe614826b82134f58df2f783249 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 20:36:38 +0100 Subject: [PATCH 6/8] one-hash tweaks --- src/backend/access/brin/brin_bloom.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index 73f8478b4c..d5581abf7b 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -276,7 +276,7 @@ typedef struct BloomFilter uint8 nhashes; /* number of hash functions */ uint32 nbits; /* number of bits in the bitmap (size) */ uint32 nbits_set; /* number of bits set to 1 */ - uint16 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ + uint32 partlens[BLOOM_MAX_NUM_PARTITIONS]; /* partition lengths */ /* data of the bloom filter (used both for sorted and hashed phase) */ char data[FLEXIBLE_ARRAY_MEMBER]; @@ -292,7 +292,7 @@ static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter); * * WIP: very naive prime sieve; could be optimized using segmented ranges */ -static uint16 * +static uint32 * generate_primes(int limit) { /* upper bound of number of primes below limit */ @@ -300,7 +300,7 @@ generate_primes(int limit) int numprimes = 1.26 * limit / log(limit); bool *is_composite = (bool *) palloc0(limit * sizeof(bool)); - uint16 *primes = (uint16 *) palloc0(numprimes * sizeof(uint16)); + uint32 *primes = (uint32 *) palloc0(numprimes * sizeof(uint32)); int maxfactor = floor(sqrt(limit)); int factor = 2; /* first prime */ @@ -338,19 +338,24 @@ generate_primes(int limit) * WIP: one-hashing bf paper ref somewhere */ static uint32 -set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) +set_bloom_partitions(int nbits, uint8 nhashes, uint32 *partlens) { int min, diff, incr; int pidx = 0; int sum = 0; int target_partlen = nbits / nhashes; + uint32 *primes; + + elog(LOG, "generating primes nbits %u nhashes %u target_partlen %d", nbits, nhashes, target_partlen); /* * Increase the limit to ensure we have some primes higher than * the target partition length. The 100 value is arbitrary, but * should be well over what we need. */ - uint16 *primes = generate_primes(target_partlen + 100); + primes = generate_primes(target_partlen + 100); + + elog(LOG, "primes generated"); /* * In our array of primes, find a sequence of length nhashes, whose @@ -358,12 +363,14 @@ set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) * array will be filled with zeros, so we need to guard against that. */ while (primes[pidx + nhashes - 1] <= target_partlen && - primes[pidx] > 0) + primes[pidx + nhashes] > 0) pidx++; for (int i = 0; i < nhashes; i++) sum += primes[pidx + i]; + elog(LOG, "nbits %d sum %d", nbits, sum); + /* * Since all the primes are less than or equal the desired partition * length, the sum is somewhat less than nbits. Increment the starting @@ -383,7 +390,9 @@ set_bloom_partitions(int nbits, uint8 nhashes, uint16 *partlens) pidx++; } - memcpy(partlens, &primes[pidx], nhashes * sizeof(uint16)); + elog(LOG, "nbits %d sum %d", nbits, sum); + + memcpy(partlens, &primes[pidx], nhashes * sizeof(uint32)); /* WIP: assuming it's not important to pfree primes */ @@ -680,7 +689,7 @@ bloom_switch_to_hashing(BloomFilter *filter) newfilter->nhashes = filter->nhashes; newfilter->nbits = filter->nbits; - memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint16)); + memcpy(newfilter->partlens, filter->partlens, filter->nhashes * sizeof(uint32)); newfilter->flags |= BLOOM_FLAG_PHASE_HASH; SET_VARSIZE(newfilter, len); -- 2.26.2
0007-BRIN-minmax-multi-indexes-20201108.patch (text/x-patch, 217.3K) download From 08de9ef122d15435ffaf78a90826f0b2d29c3871 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas@2ndquadrant.com> Date: Sat, 7 Nov 2020 15:26:11 +0100 Subject: [PATCH 7/8] BRIN minmax-multi indexes Adds a BRIN minmax-multi opclass, summarizing each range into a list of intervals, allowing more efficient handling of cases where the minmax opclasses quickly degrade. Instead of representing each range with a single interval, minmax-multi opclasses store a list of intervals and points. This allows effective handling of outliers and poorly correlated values. The number of intervals/points per range may be configured using an opclass parameter values_per_range. When building the summary, the interval are merged based on distance, determined by the new support BRIN procedure. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com --- doc/src/sgml/brin.sgml | 252 +- doc/src/sgml/ref/create_index.sgml | 11 + src/backend/access/brin/Makefile | 1 + src/backend/access/brin/brin_minmax_multi.c | 2389 +++++++++++++++++++ src/backend/access/brin/brin_tuple.c | 17 + src/include/access/brin.h | 1 + src/include/access/brin_internal.h | 3 + src/include/access/brin_tuple.h | 4 + src/include/access/transam.h | 2 +- src/include/catalog/pg_amop.dat | 544 +++++ src/include/catalog/pg_amproc.dat | 600 ++++- src/include/catalog/pg_opclass.dat | 57 + src/include/catalog/pg_opfamily.dat | 28 + src/include/catalog/pg_proc.dat | 80 + src/include/catalog/pg_type.dat | 7 + src/test/regress/expected/brin_multi.out | 421 ++++ src/test/regress/expected/psql.out | 13 +- src/test/regress/expected/type_sanity.out | 7 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/brin_multi.sql | 371 +++ 21 files changed, 4792 insertions(+), 19 deletions(-) create mode 100644 src/backend/access/brin/brin_minmax_multi.c create mode 100644 src/test/regress/expected/brin_multi.out create mode 100644 src/test/regress/sql/brin_multi.sql diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 577dd18c49..3caa30f104 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (date,date)</literal></entry></row> <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry> + <entry><literal>= (date,date)</literal></entry> + </row> + <row><entry><literal>&lt; (date,date)</literal></entry></row> + <row><entry><literal>&lt;= (date,date)</literal></entry></row> + <row><entry><literal>&gt; (date,date)</literal></entry></row> + <row><entry><literal>&gt;= (date,date)</literal></entry></row> + <row> <entry valign="middle"><literal>float4_bloom_ops</literal></entry> <entry><literal>= (float4,float4)</literal></entry> @@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry> + <entry><literal>= (float4,float4)</literal></entry> + </row> + <row><entry><literal>&lt; (float4,float4)</literal></entry></row> + <row><entry><literal>&gt; (float4,float4)</literal></entry></row> + <row><entry><literal>&lt;= (float4,float4)</literal></entry></row> + <row><entry><literal>&gt;= (float4,float4)</literal></entry></row> + <row> <entry valign="middle"><literal>float8_bloom_ops</literal></entry> <entry><literal>= (float8,float8)</literal></entry> @@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (float8,float8)</literal></entry></row> <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry> + <entry><literal>= (float8,float8)</literal></entry> + </row> + <row><entry><literal>&lt; (float8,float8)</literal></entry></row> + <row><entry><literal>&lt;= (float8,float8)</literal></entry></row> + <row><entry><literal>&gt; (float8,float8)</literal></entry></row> + <row><entry><literal>&gt;= (float8,float8)</literal></entry></row> + <row> <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry> <entry><literal>&lt;&lt; (inet,inet)</literal></entry> @@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (inet,inet)</literal></entry></row> <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry> + <entry><literal>= (inet,inet)</literal></entry> + </row> + <row><entry><literal>&lt; (inet,inet)</literal></entry></row> + <row><entry><literal>&lt;= (inet,inet)</literal></entry></row> + <row><entry><literal>&gt; (inet,inet)</literal></entry></row> + <row><entry><literal>&gt;= (inet,inet)</literal></entry></row> + <row> <entry valign="middle"><literal>int2_bloom_ops</literal></entry> <entry><literal>= (int2,int2)</literal></entry> @@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry> + <entry><literal>= (int2,int2)</literal></entry> + </row> + <row><entry><literal>&lt; (int2,int2)</literal></entry></row> + <row><entry><literal>&gt; (int2,int2)</literal></entry></row> + <row><entry><literal>&lt;= (int2,int2)</literal></entry></row> + <row><entry><literal>&gt;= (int2,int2)</literal></entry></row> + <row> <entry valign="middle"><literal>int4_bloom_ops</literal></entry> <entry><literal>= (int4,int4)</literal></entry> @@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry> + <entry><literal>= (int4,int4)</literal></entry> + </row> + <row><entry><literal>&lt; (int4,int4)</literal></entry></row> + <row><entry><literal>&gt; (int4,int4)</literal></entry></row> + <row><entry><literal>&lt;= (int4,int4)</literal></entry></row> + <row><entry><literal>&gt;= (int4,int4)</literal></entry></row> + <row> <entry valign="middle"><literal>int8_bloom_ops</literal></entry> <entry><literal>= (bigint,bigint)</literal></entry> @@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry> + <entry><literal>= (bigint,bigint)</literal></entry> + </row> + <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row> + <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row> + <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row> + <row> <entry valign="middle"><literal>interval_bloom_ops</literal></entry> <entry><literal>= (interval,interval)</literal></entry> @@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (interval,interval)</literal></entry></row> <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry> + <entry><literal>= (interval,interval)</literal></entry> + </row> + <row><entry><literal>&lt; (interval,interval)</literal></entry></row> + <row><entry><literal>&lt;= (interval,interval)</literal></entry></row> + <row><entry><literal>&gt; (interval,interval)</literal></entry></row> + <row><entry><literal>&gt;= (interval,interval)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry> <entry><literal>= (macaddr,macaddr)</literal></entry> @@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr,macaddr)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row> + <row> <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry> <entry><literal>= (macaddr8,macaddr8)</literal></entry> @@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry> + <entry><literal>= (macaddr8,macaddr8)</literal></entry> + </row> + <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row> + <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row> + <row> <entry valign="middle"><literal>name_bloom_ops</literal></entry> <entry><literal>= (name,name)</literal></entry> @@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry> + <entry><literal>= (numeric,numeric)</literal></entry> + </row> + <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row> + <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row> + <row> <entry valign="middle"><literal>oid_bloom_ops</literal></entry> <entry><literal>= (oid,oid)</literal></entry> @@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry> + <entry><literal>= (oid,oid)</literal></entry> + </row> + <row><entry><literal>&lt; (oid,oid)</literal></entry></row> + <row><entry><literal>&gt; (oid,oid)</literal></entry></row> + <row><entry><literal>&lt;= (oid,oid)</literal></entry></row> + <row><entry><literal>&gt;= (oid,oid)</literal></entry></row> + <row> <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry> <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> @@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry> + <entry><literal>= (pg_lsn,pg_lsn)</literal></entry> + </row> + <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row> + <row> <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry> <entry><literal>= (anyrange,anyrange)</literal></entry> @@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry> + <entry><literal>= (tid,tid)</literal></entry> + </row> + <row><entry><literal>&lt; (tid,tid)</literal></entry></row> + <row><entry><literal>&gt; (tid,tid)</literal></entry></row> + <row><entry><literal>&lt;= (tid,tid)</literal></entry></row> + <row><entry><literal>&gt;= (tid,tid)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry> <entry><literal>= (timestamp,timestamp)</literal></entry> @@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamp,timestamp)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row> + <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row> + <row> <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry> <entry><literal>= (timestamptz,timestamptz)</literal></entry> @@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry> + <entry><literal>= (timestamptz,timestamptz)</literal></entry> + </row> + <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row> + <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row> + <row> <entry valign="middle"><literal>time_bloom_ops</literal></entry> <entry><literal>= (time,time)</literal></entry> @@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (time,time)</literal></entry></row> <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry> + <entry><literal>= (time,time)</literal></entry> + </row> + <row><entry><literal>&lt; (time,time)</literal></entry></row> + <row><entry><literal>&lt;= (time,time)</literal></entry></row> + <row><entry><literal>&gt; (time,time)</literal></entry></row> + <row><entry><literal>&gt;= (time,time)</literal></entry></row> + <row> <entry valign="middle"><literal>timetz_bloom_ops</literal></entry> <entry><literal>= (timetz,timetz)</literal></entry> @@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry> + <entry><literal>= (timetz,timetz)</literal></entry> + </row> + <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row> + <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row> + <row> <entry valign="middle"><literal>uuid_bloom_ops</literal></entry> <entry><literal>= (uuid,uuid)</literal></entry> @@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> + <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry> + <entry><literal>= (uuid,uuid)</literal></entry> + </row> + <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row> + <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row> + <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row> + <row> <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry> <entry><literal>= (varbit,varbit)</literal></entry> @@ -654,13 +825,14 @@ typedef struct BrinOpcInfo </varlistentry> </variablelist> - The core distribution includes support for two types of operator classes: - minmax and inclusion. Operator class definitions using them are shipped for - in-core data types as appropriate. Additional operator classes can be - defined by the user for other data types using equivalent definitions, - without having to write any source code; appropriate catalog entries being - declared is enough. Note that assumptions about the semantics of operator - strategies are embedded in the support functions' source code. + The core distribution includes support for four types of operator classes: + minmax, multi-minmax, inclusion and bloom. Operator class definitions + using them are shipped for in-core data types as appropriate. Additional + operator classes can be defined by the user for other data types using + equivalent definitions, without having to write any source code; + appropriate catalog entries being declared is enough. Note that + assumptions about the semantics of operator strategies are embedded in the + support functions' source code. </para> <para> @@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo and return a hash of the value. </para> + <para> + The multi-minmax operator class is also intended for data types implementing + a totally ordered sets, and may be seen as a simple extension of the minmax + operator class. While minmax operator class summarizes values from each block + range into a single contiguous interval, multi-minmax allows summarization + into multiple smaller intervals to improve handling of outlier values. + It is possible to use the multi-minmax support procedures alongside the + corresponding operators, as shown in + <xref linkend="brin-extensibility-multi-minmax-table"/>. + All operator class members (procedures and operators) are mandatory. + </para> + + <table id="brin-extensibility-multi-minmax-table"> + <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Operator class member</entry> + <entry>Object</entry> + </row> + </thead> + <tbody> + <row> + <entry>Support Procedure 1</entry> + <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry> + </row> + <row> + <entry>Support Procedure 2</entry> + <entry>internal function <function>brin_minmax_multi_add_value()</function></entry> + </row> + <row> + <entry>Support Procedure 3</entry> + <entry>internal function <function>brin_minmax_multi_consistent()</function></entry> + </row> + <row> + <entry>Support Procedure 4</entry> + <entry>internal function <function>brin_minmax_multi_union()</function></entry> + </row> + <row> + <entry>Support Procedure 11</entry> + <entry>function to compute distance between two values (length of a range)</entry> + </row> + <row> + <entry>Operator Strategy 1</entry> + <entry>operator less-than</entry> + </row> + <row> + <entry>Operator Strategy 2</entry> + <entry>operator less-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 3</entry> + <entry>operator equal-to</entry> + </row> + <row> + <entry>Operator Strategy 4</entry> + <entry>operator greater-than-or-equal-to</entry> + </row> + <row> + <entry>Operator Strategy 5</entry> + <entry>operator greater-than</entry> + </row> + </tbody> + </tgroup> + </table> + <para> Both minmax and inclusion operator classes support cross-data-type operators, though with these the dependencies become more complicated. diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index fea13791d6..660769ef5e 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -590,6 +590,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= </listitem> </varlistentry> + <varlistentry> + <term><literal>values_per_range</literal></term> + <listitem> + <para> + Defines the maximum number of values stored by <acronym>BRIN</acronym> + minmax indexes to summarize a block range. Each value may represent + eiter a point, or boundary of an interval. Values must be be between + 16 and 256, and the default value is 32. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index 6b56131215..a386cb71f1 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -17,6 +17,7 @@ OBJS = \ brin_bloom.o \ brin_inclusion.o \ brin_minmax.o \ + brin_minmax_multi.o \ brin_pageops.o \ brin_revmap.o \ brin_tuple.o \ diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c new file mode 100644 index 0000000000..f9a7ae6608 --- /dev/null +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -0,0 +1,2389 @@ +/* + * brin_minmax_multi.c + * Implementation of Multi Min/Max opclass for BRIN + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * Implements a variant of minmax opclass, where the summary is composed of + * multiple smaller intervals. This allows us to handle outliers, which + * usually makes the simple minmax opclass inefficient. + * + * Consider for example page range with simple minmax interval [1000,2000], + * and assume a new row gets inserted into the range with value 1000000. + * Due to that the interval gets [1000,1000000]. I.e. the minmax interval + * got 1000x wider and won't be useful to eliminate scan keys between 2001 + * and 1000000. + * + * With multi-minmax opclass, we may have [1000,2000] interval initially, + * but after adding the new row we start tracking it as two interval: + * + * [1000,2000] and [1000000,1000000] + * + * This allow us to still eliminate the page range when the scan keys hit + * the gap between 2000 and 1000000, making it useful in cases when the + * simple minmax opclass gets inefficient. + * + * The number of intervals tracked per page range is somewhat flexible. + * What is restricted is the number of values per page range, and the limit + * is currently 64 (see values_per_range reloption). Collapsed intervals + * (with equal minimum and maximum value) are stored as a single value, + * while regular intervals require two values. + * + * When the number of values gets too high (by adding new values to the + * summary), we merge some of the intervals to free space for more values. + * This is done in a greedy way - we simply pick the two closest intervals, + * merge them, and repeat this until the number of values to store gets + * sufficiently low (below 75% of maximum values), but that is mostly + * arbitrary threshold and may be changed easily). + * + * To pick the closest intervals we use the "distance" support procedure, + * which measures space between two ranges (i.e. length of an interval). + * The computed value may be an approximation - in the worst case we will + * merge two ranges that are slightly less optimal at that step, but the + * index should still produce correct results. + * + * + * IDENTIFICATION + * src/backend/access/brin/brin_minmax_multi.c + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/reloptions.h" +#include "access/stratnum.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_amop.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/inet.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/pg_lsn.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/uuid.h" + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */ +#define PROCNUM_DISTANCE 11 /* required, distance between values*/ + +/* + * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + + +typedef struct MinmaxMultiOpaque +{ + FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS]; + bool extra_proc_missing[MINMAX_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; +} MinmaxMultiOpaque; + +/* + * Storage type for BRIN's minmax reloptions + */ +typedef struct MinMaxOptions +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int valuesPerRange; /* number of values per range */ +} MinMaxOptions; + +#define MINMAX_DEFAULT_VALUES_PER_PAGE 32 + +#define MinMaxGetValuesPerRange(opts) \ + ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \ + ((MinMaxOptions *) (opts))->valuesPerRange : \ + MINMAX_DEFAULT_VALUES_PER_PAGE) + + + +/* + * The summary of multi-minmax indexes has two representations - Ranges for + * convenient processing, and SerializedRanges for storage in bytea value. + * + * The Ranges struct stores the boundary values in a single array, but we + * treat regular and single-point ranges differently to save space. For + * regular ranges (with different boundary values) we have to store both + * values, while for "single-point ranges" we only need to save one value. + * + * The 'values' array stores boundary values for regular ranges first (there + * are 2*nranges values to store), and then the nvalues boundary values for + * single-point ranges. That is, we have (2*nranges + nvalues) boundary + * values in the array. + * + * +---------------------------------+-------------------------------+ + * | ranges (sorted pairs of values) | sorted values (single points) | + * +---------------------------------+-------------------------------+ + * + * This allows us to quickly add new values, and store outliers without + * making the other ranges very wide. + * + * We never store more than maxvalues values (as set by values_per_range + * reloption). If needed we merge some of the ranges. + * + * To minimize palloc overhead, we always allocate the full array with + * space for maxvalues elements. This should be fine as long as the + * maxvalues is reasonably small (64 seems fine), which is the case + * thanks to values_per_range reloption being limited to 256. + */ +typedef struct Ranges +{ + 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) */ + + /* values stored for this range - either raw values, or ranges */ + Datum values[FLEXIBLE_ARRAY_MEMBER]; +} Ranges; + +/* + * On-disk the summary is stored as a bytea value, represented by the + * SerializedRanges structure. It has a 4B varlena header, so can treated + * as varlena directly. + * + * See range_serialize/range_deserialize methods for serialization details. + */ +typedef struct 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; + +static SerializedRanges *range_serialize(Ranges *range); + +static Ranges *range_deserialize(SerializedRanges *range); + +/* Cache for support and strategy procesures. */ + +static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); + +static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, + uint16 attno, Oid subtype, uint16 strategynum); + + +/* + * minmax_multi_init + * Initialize the deserialized range list, allocate all the memory. + * + * This is only in-memory representation of the ranges, so we allocate + * everything enough space for the maximum number of values (so as not + * to have to do repallocs as the ranges grow). + */ +static Ranges * +minmax_multi_init(int maxvalues) +{ + Size len; + Ranges * ranges; + + Assert(maxvalues > 0); + + len = offsetof(Ranges, values); /* fixed header */ + len += maxvalues * sizeof(Datum); /* Datum values */ + + ranges = (Ranges *) palloc0(len); + + ranges->maxvalues = maxvalues; + + return ranges; +} + +/* + * range_serialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static SerializedRanges * +range_serialize(Ranges *range) +{ + Size len; + int nvalues; + SerializedRanges *serialized; + Oid typid; + int typlen; + bool typbyval; + + int i; + char *ptr; + + /* simple sanity checks */ + Assert(range->nranges >= 0); + Assert(range->nvalues >= 0); + Assert(range->maxvalues > 0); + + /* see how many Datum values we actually have */ + nvalues = 2*range->nranges + range->nvalues; + + Assert(2*range->nranges + range->nvalues <= range->maxvalues); + + typid = range->typid; + typbyval = get_typbyval(typid); + typlen = get_typlen(typid); + + /* header is always needed */ + len = offsetof(SerializedRanges,data); + + /* + * The space needed depends on data type - for fixed-length data types + * (by-value and some by-reference) it's pretty simple, just multiply + * (attlen * nvalues) and we're done. For variable-length by-reference + * types we need to actually walk all the values and sum the lengths. + */ + if (typlen == -1) /* varlena */ + { + int i; + for (i = 0; i < nvalues; i++) + { + len += VARSIZE_ANY(range->values[i]); + } + } + else if (typlen == -2) /* cstring */ + { + int i; + for (i = 0; i < nvalues; i++) + { + /* don't forget to include the null terminator ;-) */ + len += strlen(DatumGetPointer(range->values[i])) + 1; + } + } + else /* fixed-length types (even by-reference) */ + { + Assert(typlen > 0); + len += nvalues * typlen; + } + + /* + * Allocate the serialized object, copy the basic information. The + * serialized object is a varlena, so update the header. + */ + serialized = (SerializedRanges *) palloc0(len); + SET_VARSIZE(serialized, len); + + serialized->typid = typid; + serialized->nranges = range->nranges; + serialized->nvalues = range->nvalues; + serialized->maxvalues = range->maxvalues; + + /* + * And now copy also the boundary values (like the length calculation + * this depends on the particular data type). + */ + ptr = serialized->data; /* start of the serialized data */ + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(ptr, &range->values[i], typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + memcpy(ptr, DatumGetPointer(range->values[i]), typlen); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i])); + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + else if (typlen == -2) /* cstring */ + { + int tmp = strlen(DatumGetPointer(range->values[i])) + 1; + memcpy(ptr, DatumGetPointer(range->values[i]), tmp); + ptr += tmp; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + len)); + } + + /* exact size */ + Assert(ptr == ((char *)serialized + len)); + + return serialized; +} + +/* + * range_deserialize + * Serialize the in-memory representation into a compact varlena value. + * + * Simply copy the header and then also the individual values, as stored + * in the in-memory value array. + */ +static Ranges * +range_deserialize(SerializedRanges *serialized) +{ + int i, + nvalues; + char *ptr; + bool typbyval; + int typlen; + + Ranges *range; + + Assert(serialized->nranges >= 0); + Assert(serialized->nvalues >= 0); + Assert(serialized->maxvalues > 0); + + nvalues = 2*serialized->nranges + serialized->nvalues; + + Assert(nvalues <= serialized->maxvalues); + + range = minmax_multi_init(serialized->maxvalues); + + /* copy the header info */ + range->nranges = serialized->nranges; + range->nvalues = serialized->nvalues; + range->maxvalues = serialized->maxvalues; + range->typid = serialized->typid; + + typbyval = get_typbyval(serialized->typid); + typlen = get_typlen(serialized->typid); + + /* + * And now deconstruct the values into Datum array. We don't need + * to copy the values and will instead just point the values to the + * serialized varlena value (assuming it will be kept around). + */ + ptr = serialized->data; + + for (i = 0; i < nvalues; i++) + { + if (typbyval) /* simple by-value data types */ + { + memcpy(&range->values[i], ptr, typlen); + ptr += typlen; + } + else if (typlen > 0) /* fixed-length by-ref types */ + { + /* no copy, just set the value to the pointer */ + range->values[i] = PointerGetDatum(ptr); + ptr += typlen; + } + else if (typlen == -1) /* varlena */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += VARSIZE_ANY(DatumGetPointer(range->values[i])); + } + else if (typlen == -2) /* cstring */ + { + range->values[i] = PointerGetDatum(ptr); + ptr += strlen(DatumGetPointer(range->values[i])) + 1; + } + + /* make sure we haven't overflown the buffer end */ + Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized))); + } + + /* should have consumed the whole input value exactly */ + Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized))); + + /* return the deserialized value */ + return range; +} + +typedef struct compare_context +{ + FmgrInfo *cmpFn; + Oid colloid; +} compare_context; + +/* + * Used to represent ranges expanded during merging and combining (to + * reduce number of boundary values to store). + * + * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to + * something ExpandedRange or so. + */ +typedef struct CombineRange +{ + Datum minval; /* lower boundary */ + Datum maxval; /* upper boundary */ + bool collapsed; /* true if minval==maxval */ +} CombineRange; + +/* + * compare_combine_ranges + * Compare the combine ranges - first by minimum, then by maximum. + * + * We do guarantee that ranges in a single Range object do not overlap, + * so it may seem strange that we don't order just by minimum. But when + * merging two Ranges (which happens in the union function), the ranges + * may in fact overlap. So we do compare both. + */ +static int +compare_combine_ranges(const void *a, const void *b, void *arg) +{ + CombineRange *ra = (CombineRange *)a; + CombineRange *rb = (CombineRange *)b; + Datum r; + + compare_context *cxt = (compare_context *)arg; + + /* 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)) + return 1; + + /* 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)) + return 1; + + return 0; +} + +/* + * range_contains_value + * See if the new value is already contained in the range list. + * + * We first inspect the list of intervals. We use a small trick - we check + * the value against min/max of the whole range (min of the first interval, + * max of the last one) first, and only inspect the individual intervals if + * this passes. + * + * If the value matches none of the intervals, we check the exact values. + * We simply loop through them and invoke equality operator on them. + * + * XXX This might benefit from the fact that both the intervals and exact + * values are sorted - we might do bsearch or something. Currently that + * does not make much difference (there are only ~32 intervals), but if + * this gets increased and/or the comparator function is more expensive, + * it might be a huge win. + */ +static bool +range_contains_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + int i; + FmgrInfo *cmpLessFn; + FmgrInfo *cmpGreaterFn; + FmgrInfo *cmpEqualFn; + Oid typid = attr->atttypid; + + /* + * First inspect the ranges, if there are any. We first check the whole + * range, and only when there's still a chance of getting a match we + * inspect the individual ranges. + */ + if (ranges->nranges > 0) + { + Datum compar; + bool match = true; + + Datum minvalue = ranges->values[0]; + Datum maxvalue = ranges->values[2*ranges->nranges - 1]; + + /* + * 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. + */ + cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in the range list */ + if (DatumGetBool(compar)) + match = false; + + /* + * And now compare it to the existing maximum (last value in the + * data array). But only if we haven't already ruled out a possible + * match in the minvalue check. + */ + if (match) + { + cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + if (DatumGetBool(compar)) + match = false; + } + + /* + * So it's in the general range, but is it actually covered by any + * of the ranges? Repeat the check for each range. + * + * XXX We simply walk the ranges sequentially, but maybe we could + * further leverage the ordering and non-overlap and use bsearch to + * speed this up a bit. + */ + for (i = 0; i < ranges->nranges && match; i++) + { + /* copy the min/max values from the ranges */ + minvalue = ranges->values[2*i]; + maxvalue = ranges->values[2*i+1]; + + /* + * 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. + */ + compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + continue; + + compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + continue; + + /* hey, we found a matching row */ + return true; + } + } + + cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid, + BTEqualStrategyNumber); + + /* + * We're done with the ranges, now let's inspect the exact values. + * + * XXX Again, we do sequentially search the values - consider leveraging + * the ordering of values to improve performance. + */ + for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++) + { + Datum compar; + + compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]); + + /* found an exact match */ + if (DatumGetBool(compar)) + return true; + } + + /* the value is not covered by this BRIN tuple */ + return false; +} + +/* + * insert_value + * Adds a new value into the single-point part, while maintaining ordering. + * + * The function inserts the new value to the right place in the single-point + * part of the range. It assumes there's enough free space, and then does + * essentially an insert-sort. + * + * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that + * only the first nvalues are used. + */ +static void +insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues, + Datum newvalue) +{ + int i; + Datum lt; + + /* If there are no values yet, store the new one and we're done. */ + if (!nvalues) + { + values[0] = newvalue; + return; + } + + /* + * A common case is that the new value is entirely out of the existing + * range, i.e. it's either smaller or larger than all previous values. + * So we check and handle this case first - first we check the larger + * case, because in that case we can just append the value to the end + * of the array and we're done. + */ + + /* Is it greater than all existing values in the array? */ + lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue); + if (DatumGetBool(lt)) + { + /* just copy it in-place and we're done */ + values[nvalues] = newvalue; + return; + } + + /* + * OK, I lied a bit - we won't check the smaller case explicitly, but + * we'll just compare the value to all existing values in the array. + * But we happen to start with the smallest value, so we're actually + * doing the check anyway. + * + * XXX We do walk the values sequentially. Perhaps we could/should be + * smarter and do some sort of bisection, to improve performance? + */ + for (i = 0; i < nvalues; i++) + { + lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]); + if (DatumGetBool(lt)) + { + /* + * Move values to make space for the new entry, which should go + * to index 'i'. Entries 0 ... (i-1) should stay where they are. + */ + memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum)); + values[i] = newvalue; + return; + } + } + + /* We should never really get here. */ + Assert(false); +} + +/* + * Check that the order of the array values is correct, using the cmp + * function (which should be BTLessStrategyNumber). + */ +static void +AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues) +{ +#ifdef USE_ASSERT_CHECKING + int i; + Datum lt; + + for (i = 0; i < (nvalues-1); i++) + { + lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]); + Assert(DatumGetBool(lt)); + } +#endif +} + +/* + * Check ordering of boundary values in both parts of the ranges, using + * the cmp function (which should be BTLessStrategyNumber). + * + * XXX We might also check that the ranges and points do not overlap. But + * that will break this check sooner or later anyway. + */ +static void +AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges) +{ +#ifdef USE_ASSERT_CHECKING + /* first the ranges (there are 2*nranges boundary values) */ + AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges); + + /* then the single-point ranges (with nvalues boundar values ) */ + AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges], + ranges->nvalues); +#endif +} + +/* + * Check that the expanded ranges (built when reducing the number of ranges + * by combining some of them) are correctly sorted and do not overlap. + */ +static void +AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno, + Form_pg_attribute attr, CombineRange *ranges, + int nranges) +{ +#ifdef USE_ASSERT_CHECKING + int i; + FmgrInfo *eq; + FmgrInfo *lt; + + eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTEqualStrategyNumber); + + lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * Each range independently should be valid, i.e. that for the boundary + * 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)); + } + + /* + * And the ranges should be ordered and must nor overlap, i.e. + * upper < lower for boundaries of consecutive ranges. + */ + 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); + + Assert(DatumGetBool(r)); + } +#endif +} + +/* + * Expand ranges from Ranges into CombineRange array. This expects the + * cranges to be pre-allocated and sufficiently large (there needs to be + * at least (nranges + nvalues) slots). + */ +static void +fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges) +{ + int idx; + int i; + + idx = 0; + for (i = 0; i < ranges->nranges; i++) + { + cranges[idx].minval = ranges->values[2*i]; + cranges[idx].maxval = ranges->values[2*i+1]; + cranges[idx].collapsed = false; + idx++; + + Assert(idx <= ncranges); + } + + for (i = 0; i < ranges->nvalues; i++) + { + cranges[idx].minval = ranges->values[2*ranges->nranges + i]; + cranges[idx].maxval = ranges->values[2*ranges->nranges + i]; + cranges[idx].collapsed = true; + idx++; + + Assert(idx <= ncranges); + } + + Assert(idx == ncranges); + + return; +} + +/* + * Sort combine ranges using qsort (with BTLessStrategyNumber function). + */ +static void +sort_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + compare_context cxt; + + /* sort the values */ + cxt.colloid = colloid; + cxt.cmpFn = cmp; + + qsort_arg(cranges, ncranges, sizeof(CombineRange), + compare_combine_ranges, (void *) &cxt); +} + +/* + * When combining multiple Range values (in union function), some of the + * ranges may overlap. We simply merge the overlapping ranges to fix that. + * + * XXX This assumes the combine ranges were previously sorted (by minval + * and then maxval). We leverage this when detecting overlap. + */ +static int +merge_combine_ranges(FmgrInfo *cmp, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int idx; + + /* TODO: add assert checking the ordering of input ranges */ + + /* try merging ranges (idx) and (idx+1) if they overlap */ + idx = 0; + while (idx < (ncranges-1)) + { + Datum r; + + /* + * comparing [?,maxval] vs. [minval,?] - the ranges overlap + * if (minval < maxval) + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].minval); + + /* + * Nope, maxval < minval, so no overlap. And we know the ranges + * are ordered, so there are no more overlaps, because all the + * remaining ranges have greater or equal minval. + */ + if (DatumGetBool(r)) + { + /* proceed to the next range */ + idx += 1; + continue; + } + + /* + * So ranges 'idx' and 'idx+1' do overlap, but we don't know if + * 'idx+1' is contained in 'idx', or if they only overlap only + * partially. So compare the upper bounds and keep the larger one. + */ + r = FunctionCall2Coll(cmp, colloid, + cranges[idx].maxval, + cranges[idx+1].maxval); + + if (DatumGetBool(r)) + cranges[idx].maxval = cranges[idx+1].maxval; + + /* + * The range certainly is no longer collapsed (irrespectedly of + * the previous state). + */ + cranges[idx].collapsed = false; + + /* + * Now get rid of the (idx+1) range entirely by shifting the + * remaining ranges by 1. There are ncranges elements, and we + * need to move elements from (idx+2). That means the number + * of elements to move is [ncranges - (idx+2)]. + */ + memmove(&cranges[idx+1], &cranges[idx+2], + (ncranges - (idx + 2)) * sizeof(CombineRange)); + + /* + * Decrease the number of ranges, and repeat (with the same range, + * as it might overlap with additional ranges thanks to the merge). + */ + ncranges--; + } + + /* TODO: add assert checking the ordering etc. of produced ranges */ + + return ncranges; +} + +/* + * Represents a distance between two ranges (identified by index into + * an array of combine ranges). + */ +typedef struct DistanceValue +{ + int index; + double value; +} DistanceValue; + +/* + * Simple comparator for distance values, comparing the double value. + */ +static int +compare_distances(const void *a, const void *b) +{ + DistanceValue *da = (DistanceValue *)a; + DistanceValue *db = (DistanceValue *)b; + + if (da->value < db->value) + return -1; + else if (da->value > db->value) + return 1; + + return 0; +} + +/* + * Given an array of combine ranges, compute distance of the gaps betwen + * the ranges - for ncranges there are (ncranges-1) gaps. + * + * We simply call the "distance" function to compute the (min-max) for pairs + * of consecutive ganges. The function may be fairly expensive, so we do that + * just once (and then use it to pick as many ranges to merge as possible). + * + * See reduce_combine_ranges for details. + */ +static DistanceValue * +build_distances(FmgrInfo *distanceFn, Oid colloid, + CombineRange *cranges, int ncranges) +{ + int i; + DistanceValue *distances; + + Assert(ncranges >= 2); + + distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1)); + + /* + * Walk though the ranges once and compute distance between the ranges + * so that we can sort them once. + */ + for (i = 0; i < (ncranges-1); i++) + { + Datum a1, a2, r; + + a1 = cranges[i].maxval; + a2 = cranges[i+1].minval; + + /* compute length of the empty gap (distance between max/min) */ + r = FunctionCall2Coll(distanceFn, colloid, a1, a2); + + /* remember the index */ + distances[i].index = i; + distances[i].value = DatumGetFloat8(r); + } + + /* sort the distances in ascending order */ + pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances); + + return distances; +} + +/* + * Builds combine ranges for the existing ranges (and single-point ranges), + * and also the new value (which did not fit into the array). + * + * XXX We do perform qsort on all the values, but we could also leverage + * the fact that the input data is already sorted and do merge sort. + */ +static CombineRange * +build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges, + Datum newvalue, int *nranges) +{ + int ncranges; + CombineRange *cranges; + + /* now do the actual merge sort */ + ncranges = ranges->nranges + ranges->nvalues + 1; + cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange)); + *nranges = ncranges; + + /* put the new value at the beginning */ + cranges[0].minval = newvalue; + cranges[0].maxval = newvalue; + cranges[0].collapsed = true; + + /* then the regular and collapsed ranges */ + fill_combine_ranges(&cranges[1], ncranges-1, ranges); + + /* and sort the ranges */ + sort_combine_ranges(cmp, colloid, cranges, ncranges); + + return cranges; +} + +/* + * Counts bondary values needed to store the ranges. Each single-point + * range is stored using a single value, each regular range needs two. + */ +static int +count_values(CombineRange *cranges, int ncranges) +{ + int i; + int count; + + count = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + count += 1; + else + count += 2; + } + + return count; +} + +/* + * Combines ranges until the number of boundary values drops below 75% + * of the capacity (as set by values_per_range reloption). + * + * XXX The ranges to merge are selected solely using the distance. But + * that may not be the best strategy, for example when multiple gaps + * are of equal (or very similar) length. + * + * Consider for example points 1, 2, 3, .., 64, which have gaps of the + * same length 1 of course. In that case we tend to pick the first + * gap of that length, which leads to this: + * + * step 1: [1, 2], 3, 4, 5, .., 64 + * step 2: [1, 3], 4, 5, .., 64 + * step 3: [1, 4], 5, .., 64 + * ... + * + * So in the end we'll have one "large" range and multiple small points. + * That may be fine, but it seems a bit strange and non-optimal. Maybe + * we should consider other things when picking ranges to merge - e.g. + * length of the ranges? Or perhaps randomize the choice of ranges, with + * probability inversely proportional to the distance (the gap lengths + * may be very close, but not exactly the same). + */ +static int +reduce_combine_ranges(CombineRange *cranges, int ncranges, + DistanceValue *distances, int max_values) +{ + int i; + int ndistances = (ncranges - 1); + int count = count_values(cranges, ncranges); + + /* + * We have one fewer 'gaps' than the ranges. We'll be decrementing + * the number of combine ranges (reduction is the primary goal of + * this function), so we must use a separate value. + */ + for (i = 0; i < ndistances; i++) + { + int j; + int shortest; + + if (count <= max_values * 0.75) + break; + + shortest = distances[i].index; + + /* + * The index must be still valid with respect to the current size + * of cranges array (and it always points to the first range, so + * never to the last one - hence the -1 in the condition). + */ + Assert(shortest < (ncranges - 1)); + + if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed) + count -= 2; + else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed) + count -= 1; + + /* + * Move the values to join the two selected ranges. The new range is + * definiely not collapsed but a regular range. + */ + cranges[shortest].maxval = cranges[shortest+1].maxval; + cranges[shortest].collapsed = false; + + /* shuffle the subsequent combine ranges */ + memmove(&cranges[shortest+1], &cranges[shortest+2], + (ncranges - shortest - 2) * sizeof(CombineRange)); + + /* also, shuffle all higher indexes (we've just moved the ranges) */ + for (j = i; j < ndistances; j++) + { + if (distances[j].index > shortest) + distances[j].index--; + } + + ncranges--; + + Assert(ncranges > 0); + } + + return ncranges; +} + +/* + * Store the boundary values from CombineRanges back into Range (using + * only the minimal number of values needed). + */ +static void +store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges) +{ + int i; + int idx = 0; + + /* first copy in the regular ranges */ + ranges->nranges = 0; + for (i = 0; i < ncranges; i++) + { + if (!cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->values[idx++] = cranges[i].maxval; + ranges->nranges++; + } + } + + /* now copy in the collapsed ones */ + ranges->nvalues = 0; + for (i = 0; i < ncranges; i++) + { + if (cranges[i].collapsed) + { + ranges->values[idx++] = cranges[i].minval; + ranges->nvalues++; + } + } +} + +/* + * range_add_value + * Add the new value to the multi-minmax range. + */ +static bool +range_add_value(BrinDesc *bdesc, Oid colloid, + AttrNumber attno, Form_pg_attribute attr, + Ranges *ranges, Datum newval) +{ + FmgrInfo *cmpFn, + *distanceFn; + + /* combine ranges */ + CombineRange *cranges; + int ncranges; + DistanceValue *distances; + + MemoryContext ctx; + MemoryContext oldctx; + + Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues); + + /* + * Bail out if the value already is covered by the range. + * + * We could also add values until we hit values_per_range, and then + * do the deduplication in a batch, hoping for better efficiency. But + * that would mean we actually modify the range every time, which means + * having to serialize the value, which does palloc, walks the values, + * copies them, etc. Not exactly cheap. + * + * So instead we do the check, which should be fairly cheap - assuming + * the comparator function is not very expensive. + * + * This also implies means the values array can't contain duplicities. + */ + if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval)) + return false; + + /* we'll certainly need the comparator, so just look it up now */ + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* + * If there's space in the values array, copy it in and we're done. + * + * We do want to keep the values sorted (to speed up searches), so we + * do a simple insertion sort. We could do something more elaborate, + * e.g. by sorting the values only now and then, but for small counts + * (e.g. when maxvalues is 64) this should be fine. + */ + if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues) + { + Datum *values; + + /* beginning of the 'single value' part (for convenience) */ + values = &ranges->values[2*ranges->nranges]; + + insert_value(cmpFn, colloid, values, ranges->nvalues, newval); + + ranges->nvalues++; + + /* + * Check we haven't broken the ordering of boundary values (checks + * both parts, but that doesn't hurt). + */ + AssertRangeOrdering(cmpFn, colloid, ranges); + + /* yep, we've modified the range */ + return true; + } + + /* + * Damn - the new value is not in the range yet, but we don't have space + * to just insert it. So we need to combine some of the existing ranges, + * to reduce the number of values we need to store (joining two intervals + * reduces the number of boundaries to store by 2). + * + * To do that we first construct an array of CombineRange items - each + * combine range tracks if it's a regular range or collapsed range, where + * "collapsed" means "single point." + * + * Existing ranges (we have ranges->nranges of them) map to combine ranges + * directly, while single points (ranges->nvalues of them) have to be + * expanded. We neet the combine ranges to be sorted, and we do that by + * performing a merge sort of ranges, values and new value. + */ + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + /* and we'll also need the 'distance' procedure */ + distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE); + + /* + * 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); + + /* OK build the combine ranges */ + cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges); + + /* build array of gap distances and sort them in ascending order */ + distances = build_distances(distanceFn, colloid, cranges, ncranges); + + /* + * Combine ranges until we release at least 25% of the space. This + * threshold is somewhat arbitrary, perhaps needs tuning. We must not + * use too low or high value. + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges->maxvalues); + + Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75); + + /* decompose the combine ranges into regular ranges and single values */ + store_combine_ranges(ranges, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* Check the ordering invariants are not violated (for both parts). */ + AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2); + AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2], + ranges->nvalues); + + return true; +} + +Datum +brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) +{ + BrinOpcInfo *result; + + /* + * opaque->strategy_procinfos is initialized lazily; here it is set to + * all-uninitialized by palloc0 which sets fn_oid to InvalidOid. + */ + + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + + sizeof(MinmaxMultiOpaque)); + result->oi_nstored = 1; + result->oi_regular_nulls = true; + result->oi_opaque = (MinmaxMultiOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(1)); + result->oi_typcache[0] = lookup_type_cache(PG_BRIN_MINMAX_MULTI_SUMMARYOID, 0); + + PG_RETURN_POINTER(result); +} + +/* + * Compute distance between two float4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS) +{ + float a1 = PG_GETARG_FLOAT4(0); + float a2 = PG_GETARG_FLOAT4(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two float8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS) +{ + double a1 = PG_GETARG_FLOAT8(0); + double a2 = PG_GETARG_FLOAT8(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8(a2 - a1); +} + +/* + * Compute distance between two int2 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS) +{ + int16 a1 = PG_GETARG_INT16(0); + int16 a2 = PG_GETARG_INT16(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int4 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS) +{ + int32 a1 = PG_GETARG_INT32(0); + int32 a2 = PG_GETARG_INT32(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two int8 values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS) +{ + int64 a1 = PG_GETARG_INT64(0); + int64 a2 = PG_GETARG_INT64(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(a1 <= a2); + + PG_RETURN_FLOAT8((double) a2 - (double) a1); +} + +/* + * Compute distance between two tid values (by mapping them to float8 + * and then subtracting them). + */ +Datum +brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS) +{ + double da1, + da2; + + ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0); + ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(ItemPointerCompare(pa1, pa2) <= 0); + + da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa1); + + da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage + + ItemPointerGetOffsetNumber(pa2); + + PG_RETURN_FLOAT8(da2 - da1); +} + +/* + * Comutes distance between two numeric values (plain subtraction). + */ +Datum +brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS) +{ + Datum d; + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2))); + + d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */ + + PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d)); +} + +/* + * Computes approximate distance between two UUID values. + * + * XXX We do not need a perfectly accurate value, so we approximate the + * deltas (which would have to be 128-bit integers) with a 64-bit float. + * The small inaccuracies do not matter in practice, in the worst case + * we'll decide to merge ranges that are not the closest ones. + */ +Datum +brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) +{ + int i; + double delta = 0; + + Datum a1 = PG_GETARG_DATUM(0); + Datum a2 = PG_GETARG_DATUM(1); + + pg_uuid_t *u1 = DatumGetUUIDP(a1); + pg_uuid_t *u2 = DatumGetUUIDP(a2); + + /* + * We know the values are range boundaries, but the range may be + * collapsed (i.e. single points), with equal values. + */ + Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2))); + + /* compute approximate delta as a double precision value */ + for (i = UUID_LEN-1; i >= 0; i--) + { + delta += (int) u2->data[i] - (int) u1->data[i]; + delta /= 256; + } + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two time (without tz) values. + * + * TimeADT is just an int64, so we simply subtract the values directly. + */ +Datum +brin_minmax_multi_distance_time(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ta = PG_GETARG_TIMEADT(0); + TimeADT tb = PG_GETARG_TIMEADT(1); + + delta = (tb - ta); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes approximate distance between two timetz values. + * + * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT. + * + * XXX Does this need to consider the time zones? + */ +Datum +brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0); + TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1); + + delta = tb->time - ta->time; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Computes distance between two interval values. + * + * Intervals are internally just 'time' values, so use the same approach + * as for in brin_minmax_multi_distance_time. + * + * XXX Do we actually need two separate functions, then? + */ +Datum +brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) +{ + double delta = 0; + + TimeADT ia = PG_GETARG_TIMEADT(0); + TimeADT ib = PG_GETARG_TIMEADT(1); + + delta = (ib - ia); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two pg_lsn values. + * + * LSN is just an int64 encoding position in the stream, so just subtract + * those int64 values directly. + */ +Datum +brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS) +{ + double delta = 0; + + XLogRecPtr lsna = PG_GETARG_LSN(0); + XLogRecPtr lsnb = PG_GETARG_LSN(1); + + delta = (lsnb - lsna); + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr values. + * + * mac addresses are treated as 6 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr *a = PG_GETARG_MACADDR_P(0); + macaddr *b = PG_GETARG_MACADDR_P(1); + + delta = ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two macaddr8 values. + * + * macaddr8 addresses are 8 unsigned chars, so do the same thing we + * already do for UUID values. + */ +Datum +brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS) +{ + double delta; + + macaddr8 *a = PG_GETARG_MACADDR8_P(0); + macaddr8 *b = PG_GETARG_MACADDR8_P(1); + + delta = ((double)b->h - (double)a->h); + delta /= 256; + + delta += ((double)b->g - (double)a->g); + delta /= 256; + + delta += ((double)b->f - (double)a->f); + delta /= 256; + + delta += ((double)b->e - (double)a->e); + delta /= 256; + + delta += ((double)b->d - (double)a->d); + delta /= 256; + + delta += ((double)b->c - (double)a->c); + delta /= 256; + + delta += ((double)b->b - (double)a->b); + delta /= 256; + + delta += ((double)b->a - (double)a->a); + delta /= 256; + + Assert(delta >= 0); + + PG_RETURN_FLOAT8(delta); +} + +/* + * Compute distance between two inet values. + * + * The distance is defined as difference between 32-bit/128-bit values, + * depending on the IP version. The distance is computed by subtracting + * the bytes and normalizing it to [0,1] range for each IP family. + * Addresses from difference families are consider to be in maximum + * distance, which is 1.0. + * + * XXX Does this need to consider the mask (bits)? For now it's ignored. + */ +Datum +brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS) +{ + double delta; + int i; + int len; + unsigned char *addra, + *addrb; + + inet *ipa = PG_GETARG_INET_PP(0); + inet *ipb = PG_GETARG_INET_PP(1); + + /* + * If the addresses are from different families, consider them to be + * in maximal possible distance (which is 1.0). + */ + if (ip_family(ipa) != ip_family(ipb)) + return 1.0; + + /* ipv4 or ipv6 */ + if (ip_family(ipa) == PGSQL_AF_INET) + len = 4; + else + len = 16; /* NS_IN6ADDRSZ */ + + addra = ip_addr(ipa); + addrb = ip_addr(ipb); + + delta = 0; + for (i = len-1; i >= 0; i--) + { + delta += (double)addrb[i] - (double)addra[i]; + delta /= 256; + } + + Assert((delta >= 0) && (delta <= 1)); + + PG_RETURN_FLOAT8(delta); +} + +static void +brin_minmax_multi_serialize(Datum src, Datum *dst) +{ + Ranges *ranges = (Ranges *) DatumGetPointer(src); + SerializedRanges *s = range_serialize(ranges); + dst[0] = PointerGetDatum(s); +} + +static int +brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts) +{ + return MinMaxGetValuesPerRange(opts); +} + +/* + * Examine the given index tuple (which contains 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); + MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + bool modified = false; + Form_pg_attribute attr; + AttrNumber attno; + Ranges *ranges; + SerializedRanges *serialized = NULL; + + Assert(!isnull); + + attno = column->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + /* use the already deserialized value, is 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. + */ + if (column->bv_allnulls) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts)); + ranges->typid = attr->atttypid; + + MemoryContextSwitchTo(oldctx); + + column->bv_allnulls = false; + modified = true; + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + } + else if (!ranges) + { + MemoryContext oldctx; + + oldctx = MemoryContextSwitchTo(column->bv_context); + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + column->bv_mem_value = PointerGetDatum(ranges); + column->bv_serialize = brin_minmax_multi_serialize; + + MemoryContextSwitchTo(oldctx); + } + + /* + * Try to add the new value to the range. We need to update the modified + * flag, so that we serialize the correct value. + */ + 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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(), + subtype; + AttrNumber attno; + Datum value; + FmgrInfo *finfo; + SerializedRanges *serialized; + Ranges *ranges; + int keyno; + int rangeno; + int i; + + attno = column->bv_attno; + + serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]); + ranges = range_deserialize(serialized); + + /* 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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 = 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, + BTLessStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, minval); + + /* smaller than the smallest value in this range */ + if (DatumGetBool(compar)) + break; + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + BTGreaterStrategyNumber); + compar = FunctionCall2Coll(cmpFn, colloid, value, maxval); + + /* larger than the largest value in this range */ + if (DatumGetBool(compar)) + break; + + /* 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 = FunctionCall2Coll(finfo, colloid, maxval, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + /* and now inspect the 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; + + for (keyno = 0; keyno < nkeys; keyno++) + { + Datum matches; + ScanKey key = keys[keyno]; + + /* 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: + + finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + matches = FunctionCall2Coll(finfo, colloid, val, value); + break; + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + matches = 0; + break; + } + + /* the range has to match all the scan keys */ + matching &= DatumGetBool(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_DATUM(BoolGetDatum(true)); + } + + PG_RETURN_DATUM(BoolGetDatum(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); + // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS(); + Oid colloid = PG_GET_COLLATION(); + SerializedRanges *serialized_a; + SerializedRanges *serialized_b; + Ranges *ranges_a; + Ranges *ranges_b; + AttrNumber attno; + Form_pg_attribute attr; + CombineRange *cranges; + int ncranges; + FmgrInfo *cmpFn, + *distanceFn; + DistanceValue *distances; + MemoryContext ctx; + MemoryContext oldctx; + + Assert(col_a->bv_attno == col_b->bv_attno); + Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); + + attno = col_a->bv_attno; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + + serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]); + serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]); + + ranges_a = range_deserialize(serialized_a); + ranges_b = range_deserialize(serialized_b); + + /* make sure neither of the ranges is NULL */ + Assert(ranges_a && ranges_b); + + ncranges = (ranges_a->nranges + ranges_a->nvalues) + + (ranges_b->nranges + ranges_b->nvalues); + + /* + * 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 */ + cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange)); + + /* fill the combine ranges with entries for the first range */ + fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues, + ranges_a); + + /* and now add combine ranges for the second range */ + fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues], + ranges_b->nranges + ranges_b->nvalues, + ranges_b); + + cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid, + BTLessStrategyNumber); + + /* sort the combine ranges */ + sort_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* + * We've merged two different lists of ranges, so some of them may be + * overlapping. So walk through them and merge them. + */ + ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges); + + /* check that the combine ranges are correct (no overlaps, ordering) */ + AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges); + + /* 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, cranges, ncranges); + + /* + * See how many values would be needed to store the current ranges, and if + * needed combine as many off them to get below the maxvalues threshold. + * The collapsed ranges will be stored as a single value. + * + * XXX The maxvalues may be different, so perhaps use Max? + */ + ncranges = reduce_combine_ranges(cranges, ncranges, distances, + ranges_a->maxvalues); + + /* update the first range summary */ + store_combine_ranges(ranges_a, cranges, ncranges); + + MemoryContextSwitchTo(oldctx); + MemoryContextDelete(ctx); + + /* cleanup and update the serialized value */ + pfree(serialized_a); + col_a->bv_values[0] = PointerGetDatum(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 is not exists. + */ +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 we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + 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 + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + 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; + + Assert(strategynum >= 1 && + strategynum <= BTMaxStrategyNumber); + + opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_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; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} + +Datum +brin_minmax_multi_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(MinMaxOptions)); + + add_local_int_reloption(relopts, "values_per_range", "desc", + 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange)); + + PG_RETURN_VOID(); +} + +/* + * 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; + + initStringInfo(&str); + appendStringInfoChar(&str, '{'); + + /* + * XXX not sure the detoasting is necessary (probably not, this + * can only be in an index). + */ + ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(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 = range_deserialize(ranges); + + appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u", + ranges_deserialized->nranges, + ranges_deserialized->nvalues, + ranges_deserialized->maxvalues); + + /* serialize ranges */ + idx = 0; + for (i = 0; i < ranges_deserialized->nranges; i++) + { + Datum a, b; + text *c; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s ... %s", + DatumGetPointer(a), + DatumGetPointer(b)); + + c = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(c), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nranges > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext)); + + extval = OidOutputFunctionCall(typoutput, val); + + appendStringInfo(&str, " ranges: %s", extval); + } + + /* serialize individual values */ + astate_values = NULL; + + for (i = 0; i < ranges_deserialized->nvalues; i++) + { + Datum a; + text *b; + StringInfoData str; + + initStringInfo(&str); + + a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]); + + appendStringInfo(&str, "%s", DatumGetPointer(a)); + + b = cstring_to_text(str.data); + + astate_values = accumArrayResult(astate_values, + PointerGetDatum(b), + false, + TEXTOID, + CurrentMemoryContext); + } + + if (ranges_deserialized->nvalues > 0) + { + Oid typoutput; + bool typIsVarlena; + Datum val; + char *extval; + + getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena); + + val = PointerGetDatum(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); +} diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 6774f597a4..91833f12c5 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -159,6 +159,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple, if (tuple->bt_columns[keyno].bv_hasnulls) anynulls = true; + /* if needed, serialize the values before forming the on-disk tuple */ + if (tuple->bt_columns[keyno].bv_serialize) + { + tuple->bt_columns[keyno].bv_serialize( + tuple->bt_columns[keyno].bv_mem_value, + tuple->bt_columns[keyno].bv_values); + } + /* * Now obtain the values of each stored datum. Note that some values * might be toasted, and we cannot rely on the original heap values @@ -498,6 +506,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc) dtuple->bt_columns[i].bv_allnulls = true; dtuple->bt_columns[i].bv_hasnulls = false; dtuple->bt_columns[i].bv_values = (Datum *) currdatum; + + dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL); + dtuple->bt_columns[i].bv_serialize = NULL; + dtuple->bt_columns[i].bv_context = dtuple->bt_context; + currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored; } @@ -577,6 +590,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple) dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno]; dtup->bt_columns[keyno].bv_allnulls = false; + + dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL); + dtup->bt_columns[keyno].bv_serialize = NULL; + dtup->bt_columns[keyno].bv_context = dtup->bt_context; } MemoryContextSwitchTo(oldcxt); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index b02298c0aa..03499281c6 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -24,6 +24,7 @@ typedef struct BrinOptions bool autosummarize; double nDistinctPerRange; /* number of distinct values per range */ double falsePositiveRate; /* false positive for bloom filter */ + int valuesPerRange; /* number of values per range */ } BrinOptions; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index e3c9d47503..ee4d0706df 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -62,6 +62,9 @@ typedef struct BrinDesc double bd_nDistinctPerRange; double bd_falsePositiveRange; + /* parameters for multi-minmax indexes */ + int bd_valuesPerRange; + /* per-column info; bd_tupdesc->natts entries long */ BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER]; } BrinDesc; diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h index a9ccc3995b..064a93d09b 100644 --- a/src/include/access/brin_tuple.h +++ b/src/include/access/brin_tuple.h @@ -14,6 +14,7 @@ #include "access/brin_internal.h" #include "access/tupdesc.h" +typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst); /* * A BRIN index stores one index tuple per page range. Each index tuple @@ -27,6 +28,9 @@ typedef struct BrinValues bool bv_hasnulls; /* are there any nulls in the page range? */ bool bv_allnulls; /* are all values nulls in the page range? */ Datum *bv_values; /* current accumulated values */ + Datum bv_mem_value; /* expanded accumulated values */ + MemoryContext bv_context; + brin_serialize_callback_type bv_serialize; } BrinValues; /* diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 2f1f144db4..d3d12a7b99 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest) * ---------- */ #define FirstGenbkiObjectId 10000 -#define FirstBootstrapObjectId 12000 +#define FirstBootstrapObjectId 13000 #define FirstNormalObjectId 16384 /* diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat index 3c0df5597f..64ae71a58d 100644 --- a/src/include/catalog/pg_amop.dat +++ b/src/include/catalog/pg_amop.dat @@ -1884,6 +1884,152 @@ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', amopmethod => 'brin' }, +# minmax multi integer + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4', + amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)', + amopmethod => 'brin' }, + # bloom integer { amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8', @@ -1961,6 +2107,23 @@ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'brin' }, +# minmax multi oid +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid', + amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)', + amopmethod => 'brin' }, + # bloom oid { amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid', amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)', @@ -1987,6 +2150,22 @@ { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid', amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)', amopmethod => 'brin' }, +# minmax multi tid +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid', + amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)', + amopmethod => 'brin' }, # minmax float (float4, float8) @@ -2054,6 +2233,72 @@ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', amopmethod => 'brin' }, +# minmax multi float (float4, float8) + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float4,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float4,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '2', + amopopr => '<=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '4', + amopopr => '>=(float8,float4)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '2', + amopopr => '<=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '4', + amopopr => '>=(float8,float8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8', + amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)', + amopmethod => 'brin' }, + # bloom float { amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4', amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)', @@ -2085,6 +2330,23 @@ amoprighttype => 'macaddr', amopstrategy => '5', amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, +# minmax multi macaddr +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '1', + amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '2', + amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '3', + amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '4', + amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr', + amoprighttype => 'macaddr', amopstrategy => '5', + amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' }, + # bloom macaddr { amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr', amoprighttype => 'macaddr', amopstrategy => '1', @@ -2107,6 +2369,23 @@ amoprighttype => 'macaddr8', amopstrategy => '5', amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, +# minmax multi macaddr8 +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '1', + amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '2', + amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '3', + amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '4', + amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' }, +{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8', + amoprighttype => 'macaddr8', amopstrategy => '5', + amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' }, + # bloom macaddr8 { amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8', amoprighttype => 'macaddr8', amopstrategy => '1', @@ -2129,6 +2408,23 @@ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', amopmethod => 'brin' }, +# minmax multi inet +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet', + amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)', + amopmethod => 'brin' }, + # bloom inet { amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet', amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)', @@ -2193,6 +2489,23 @@ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', amopmethod => 'brin' }, +# minmax multi time without time zone +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time', + amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)', + amopmethod => 'brin' }, + # bloom time without time zone { amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time', amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)', @@ -2344,6 +2657,152 @@ amoprighttype => 'timestamptz', amopstrategy => '5', amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, +# minmax multi datetime (date, timestamp, timestamptz) + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)', + amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(date,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(date,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(date,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(date,timestamptz)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '1', + amopopr => '<(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '2', + amopopr => '<=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '3', + amopopr => '=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '4', + amopopr => '>=(timestamptz,date)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'date', amopstrategy => '5', + amopopr => '>(timestamptz,date)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '1', + amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '3', + amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamp', amopstrategy => '5', + amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' }, + +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '1', + amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '2', + amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '3', + amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '4', + amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz', + amoprighttype => 'timestamptz', amopstrategy => '5', + amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' }, + # bloom datetime (date, timestamp, timestamptz) { amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp', @@ -2399,6 +2858,23 @@ amoprighttype => 'interval', amopstrategy => '5', amopopr => '>(interval,interval)', amopmethod => 'brin' }, +# minmax multi interval +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '1', + amopopr => '<(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '2', + amopopr => '<=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '3', + amopopr => '=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '4', + amopopr => '>=(interval,interval)', amopmethod => 'brin' }, +{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval', + amoprighttype => 'interval', amopstrategy => '5', + amopopr => '>(interval,interval)', amopmethod => 'brin' }, + # bloom interval { amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval', amoprighttype => 'interval', amopstrategy => '1', @@ -2421,6 +2897,23 @@ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', amopmethod => 'brin' }, +# minmax multi time with time zone +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '2', + amopopr => '<=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '4', + amopopr => '>=(timetz,timetz)', amopmethod => 'brin' }, +{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz', + amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)', + amopmethod => 'brin' }, + # bloom time with time zone { amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz', amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)', @@ -2477,6 +2970,23 @@ amoprighttype => 'numeric', amopstrategy => '5', amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, +# minmax multi numeric +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '1', + amopopr => '<(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '2', + amopopr => '<=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '3', + amopopr => '=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '4', + amopopr => '>=(numeric,numeric)', amopmethod => 'brin' }, +{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric', + amoprighttype => 'numeric', amopstrategy => '5', + amopopr => '>(numeric,numeric)', amopmethod => 'brin' }, + # bloom numeric { amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric', amoprighttype => 'numeric', amopstrategy => '1', @@ -2499,6 +3009,23 @@ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', amopmethod => 'brin' }, +# minmax multi uuid +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid', + amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)', + amopmethod => 'brin' }, + # bloom uuid { amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid', amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)', @@ -2565,6 +3092,23 @@ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +# minmax multi pg_lsn +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '2', + amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '4', + amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' }, +{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn', + amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)', + amopmethod => 'brin' }, + # bloom pg_lsn { amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn', amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)', diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 15f3baea15..8d212ede8b 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -953,6 +953,152 @@ { amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4', amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi integer: int2, int4, int8 +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4', + amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom integer: int2, int4, int8 { amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8', amprocrighttype => 'int8', amprocnum => '1', @@ -1048,6 +1194,23 @@ { amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi oid +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid', + amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' }, + # bloom oid { amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid', amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' }, @@ -1094,6 +1257,23 @@ { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid', amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' }, +# minmax multi tid +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid', + amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' }, + # minmax float { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1144,6 +1324,80 @@ amprocrighttype => 'float4', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi float +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float4' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8', + amprocrighttype => 'float4', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom float { amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4', amprocrighttype => 'float4', amprocnum => '1', @@ -1197,6 +1451,26 @@ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr', + amprocrighttype => 'macaddr', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr' }, + # bloom macaddr { amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', @@ -1231,6 +1505,26 @@ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi macaddr8 +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8', + amprocrighttype => 'macaddr8', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_macaddr8' }, + # bloom macaddr8 { amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8', amprocrighttype => 'macaddr8', amprocnum => '1', @@ -1264,6 +1558,26 @@ { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi inet +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet', + amprocrighttype => 'inet', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_inet' }, + # bloom inet { amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet', amprocrighttype => 'inet', amprocnum => '1', @@ -1349,6 +1663,25 @@ { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time without time zone +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time', + amprocrighttype => 'time', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_time' }, + # bloom time without time zone { amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time', amprocrighttype => 'time', amprocnum => '1', @@ -1474,6 +1807,170 @@ amprocrighttype => 'timestamptz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi datetime (date, timestamp, timestamptz) +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'date', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamp', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date', + amprocrighttype => 'timestamptz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_float8' }, + # bloom datetime (date, timestamp, timestamptz) { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp', amprocrighttype => 'timestamp', amprocnum => '1', @@ -1544,6 +2041,26 @@ amprocrighttype => 'interval', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi interval +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval', + amprocrighttype => 'interval', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_interval' }, + # bloom interval { amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '1', @@ -1578,6 +2095,26 @@ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi time with time zone +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz', + amprocrighttype => 'timetz', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_timetz' }, + # bloom time with time zone { amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz', amprocrighttype => 'timetz', amprocnum => '1', @@ -1638,6 +2175,26 @@ amprocrighttype => 'numeric', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi numeric +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric', + amprocrighttype => 'numeric', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_numeric' }, + # bloom numeric { amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric', amprocrighttype => 'numeric', amprocnum => '1', @@ -1669,7 +2226,28 @@ amprocrighttype => 'uuid', amprocnum => '3', amproc => 'brin_minmax_consistent' }, { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid', - amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' }, + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_union' }, + +# minmax multi uuid +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid', + amprocrighttype => 'uuid', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_uuid' }, # bloom uuid { amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid', @@ -1724,6 +2302,26 @@ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'brin_minmax_union' }, +# minmax multi pg_lsn +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '1', + amproc => 'brin_minmax_multi_opcinfo' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '2', + amproc => 'brin_minmax_multi_add_value' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '3', + amproc => 'brin_minmax_multi_consistent' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '4', + amproc => 'brin_minmax_multi_union' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '5', + amproc => 'brin_minmax_multi_options' }, +{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn', + amprocrighttype => 'pg_lsn', amprocnum => '11', + amproc => 'brin_minmax_multi_distance_pg_lsn' }, + # bloom pg_lsn { amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn', amprocrighttype => 'pg_lsn', amprocnum => '1', diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat index ca747a03b9..27ef11b81b 100644 --- a/src/include/catalog/pg_opclass.dat +++ b/src/include/catalog/pg_opclass.dat @@ -275,18 +275,27 @@ { opcmethod => 'brin', opcname => 'int8_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8', opckeytype => 'int8' }, +{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8', + opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int8_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8', opckeytype => 'int8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2', opckeytype => 'int2' }, +{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2', + opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int2_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2', opckeytype => 'int2', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_minmax_ops', opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4', opckeytype => 'int4' }, +{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops', + opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4', + opckeytype => 'int4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'int4_bloom_ops', opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4', opckeytype => 'int4', opcdefault => 'f' }, @@ -298,6 +307,9 @@ opckeytype => 'text', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_minmax_ops', opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' }, +{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops', + opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid', + opckeytype => 'oid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'oid_bloom_ops', opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid', opckeytype => 'oid', opcdefault => 'f' }, @@ -306,33 +318,51 @@ { opcmethod => 'brin', opcname => 'tid_bloom_ops', opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid', opcdefault => 'f'}, +{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops', + opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid', + opckeytype => 'tid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float4', opckeytype => 'float4' }, +{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4', + opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float4_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float4', opckeytype => 'float4', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_minmax_ops', opcfamily => 'brin/float_minmax_ops', opcintype => 'float8', opckeytype => 'float8' }, +{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops', + opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8', + opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'float8_bloom_ops', opcfamily => 'brin/float_bloom_ops', opcintype => 'float8', opckeytype => 'float8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_minmax_ops', opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr', opckeytype => 'macaddr' }, +{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops', + opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr', + opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr_bloom_ops', opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr', opckeytype => 'macaddr', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops', opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8' }, +{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops', + opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8', + opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops', opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8', opckeytype => 'macaddr8', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_minmax_ops', opcfamily => 'brin/network_minmax_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet' }, +{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops', + opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet', + opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'inet_bloom_ops', opcfamily => 'brin/network_bloom_ops', opcintype => 'inet', opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' }, @@ -348,36 +378,54 @@ { opcmethod => 'brin', opcname => 'time_minmax_ops', opcfamily => 'brin/time_minmax_ops', opcintype => 'time', opckeytype => 'time' }, +{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops', + opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time', + opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'time_bloom_ops', opcfamily => 'brin/time_bloom_ops', opcintype => 'time', opckeytype => 'time', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date', opckeytype => 'date' }, +{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date', + opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'date_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date', opckeytype => 'date', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp', opckeytype => 'timestamp' }, +{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp', + opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamp_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp', opckeytype => 'timestamp', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops', opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz' }, +{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops', + opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz', + opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timestamptz_bloom_ops', opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz', opckeytype => 'timestamptz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_minmax_ops', opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval', opckeytype => 'interval' }, +{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops', + opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval', + opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'interval_bloom_ops', opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval', opckeytype => 'interval', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_minmax_ops', opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz', opckeytype => 'timetz' }, +{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops', + opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz', + opckeytype => 'timetz', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'timetz_bloom_ops', opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz', opckeytype => 'timetz', opcdefault => 'f' }, @@ -389,6 +437,9 @@ { opcmethod => 'brin', opcname => 'numeric_minmax_ops', opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric', opckeytype => 'numeric' }, +{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops', + opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric', + opckeytype => 'numeric', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'numeric_bloom_ops', opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric', opckeytype => 'numeric', opcdefault => 'f' }, @@ -398,6 +449,9 @@ { opcmethod => 'brin', opcname => 'uuid_minmax_ops', opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid', opckeytype => 'uuid' }, +{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops', + opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid', + opckeytype => 'uuid', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'uuid_bloom_ops', opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid', opckeytype => 'uuid', opcdefault => 'f' }, @@ -407,6 +461,9 @@ { opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops', opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn' }, +{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops', + opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn', + opckeytype => 'pg_lsn', opcdefault => 'f' }, { opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops', opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn', opckeytype => 'pg_lsn', opcdefault => 'f' }, diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8875079698..8e6d9eec16 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -180,10 +180,14 @@ opfmethod => 'gin', opfname => 'jsonb_path_ops' }, { oid => '4054', opfmethod => 'brin', opfname => 'integer_minmax_ops' }, +{ oid => '9015', + opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' }, { oid => '8100', opfmethod => 'brin', opfname => 'integer_bloom_ops' }, { oid => '4055', opfmethod => 'brin', opfname => 'numeric_minmax_ops' }, +{ oid => '9016', + opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' }, { oid => '4056', opfmethod => 'brin', opfname => 'text_minmax_ops' }, { oid => '8101', @@ -192,10 +196,14 @@ opfmethod => 'brin', opfname => 'numeric_bloom_ops' }, { oid => '4058', opfmethod => 'brin', opfname => 'timetz_minmax_ops' }, +{ oid => '9017', + opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' }, { oid => '8103', opfmethod => 'brin', opfname => 'timetz_bloom_ops' }, { oid => '4059', opfmethod => 'brin', opfname => 'datetime_minmax_ops' }, +{ oid => '9018', + opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' }, { oid => '8104', opfmethod => 'brin', opfname => 'datetime_bloom_ops' }, { oid => '4062', @@ -212,26 +220,38 @@ opfmethod => 'brin', opfname => 'name_bloom_ops' }, { oid => '4068', opfmethod => 'brin', opfname => 'oid_minmax_ops' }, +{ oid => '9019', + opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' }, { oid => '8108', opfmethod => 'brin', opfname => 'oid_bloom_ops' }, { oid => '4069', opfmethod => 'brin', opfname => 'tid_minmax_ops' }, { oid => '8123', opfmethod => 'brin', opfname => 'tid_bloom_ops' }, +{ oid => '9020', + opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' }, { oid => '4070', opfmethod => 'brin', opfname => 'float_minmax_ops' }, +{ oid => '9021', + opfmethod => 'brin', opfname => 'float_minmax_multi_ops' }, { oid => '8109', opfmethod => 'brin', opfname => 'float_bloom_ops' }, { oid => '4074', opfmethod => 'brin', opfname => 'macaddr_minmax_ops' }, +{ oid => '9022', + opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' }, { oid => '8110', opfmethod => 'brin', opfname => 'macaddr_bloom_ops' }, { oid => '4109', opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' }, +{ oid => '9023', + opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' }, { oid => '8111', opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' }, { oid => '4075', opfmethod => 'brin', opfname => 'network_minmax_ops' }, +{ oid => '9024', + opfmethod => 'brin', opfname => 'network_minmax_multi_ops' }, { oid => '4102', opfmethod => 'brin', opfname => 'network_inclusion_ops' }, { oid => '8112', @@ -242,10 +262,14 @@ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' }, { oid => '4077', opfmethod => 'brin', opfname => 'time_minmax_ops' }, +{ oid => '9025', + opfmethod => 'brin', opfname => 'time_minmax_multi_ops' }, { oid => '8114', opfmethod => 'brin', opfname => 'time_bloom_ops' }, { oid => '4078', opfmethod => 'brin', opfname => 'interval_minmax_ops' }, +{ oid => '9026', + opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' }, { oid => '8115', opfmethod => 'brin', opfname => 'interval_bloom_ops' }, { oid => '4079', @@ -254,12 +278,16 @@ opfmethod => 'brin', opfname => 'varbit_minmax_ops' }, { oid => '4081', opfmethod => 'brin', opfname => 'uuid_minmax_ops' }, +{ oid => '9027', + opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' }, { oid => '8116', opfmethod => 'brin', opfname => 'uuid_bloom_ops' }, { oid => '4103', opfmethod => 'brin', opfname => 'range_inclusion_ops' }, { oid => '4082', opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' }, +{ oid => '9028', + opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' }, { oid => '8117', opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' }, { oid => '4104', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 7351ecb07e..7a059dfc74 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8143,6 +8143,71 @@ proname => 'brin_minmax_union', prorettype => 'bool', proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' }, +# BRIN minmax multi +{ oid => '9029', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' }, +{ oid => '9030', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_add_value', prorettype => 'bool', + proargtypes => 'internal internal internal internal', + prosrc => 'brin_minmax_multi_add_value' }, +{ oid => '9031', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_consistent', prorettype => 'bool', + proargtypes => 'internal internal internal int4', + prosrc => 'brin_minmax_multi_consistent' }, +{ oid => '9032', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_union', prorettype => 'bool', + proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' }, +{ oid => '9033', descr => 'BRIN multi minmax support', + proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f', + proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' }, + +{ oid => '9000', descr => 'BRIN multi minmax int2 distance', + proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' }, +{ oid => '9001', descr => 'BRIN multi minmax int4 distance', + proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' }, +{ oid => '9002', descr => 'BRIN multi minmax int8 distance', + proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' }, +{ oid => '9003', descr => 'BRIN multi minmax float4 distance', + proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' }, +{ oid => '9004', descr => 'BRIN multi minmax float8 distance', + proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' }, +{ oid => '9005', descr => 'BRIN multi minmax numeric distance', + proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' }, +{ oid => '9006', descr => 'BRIN multi minmax tid distance', + proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' }, +{ oid => '9007', descr => 'BRIN multi minmax uuid distance', + proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' }, +{ oid => '9008', descr => 'BRIN multi minmax time distance', + proname => 'brin_minmax_multi_distance_time', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' }, +{ oid => '9009', descr => 'BRIN multi minmax interval distance', + proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' }, +{ oid => '9010', descr => 'BRIN multi minmax timetz distance', + proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' }, +{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance', + proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' }, +{ oid => '9012', descr => 'BRIN multi minmax macaddr distance', + proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' }, +{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance', + proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' }, +{ oid => '9014', descr => 'BRIN multi minmax inet distance', + proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8', + proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' }, + # BRIN inclusion { oid => '4105', descr => 'BRIN inclusion support', proname => 'brin_inclusion_opcinfo', prorettype => 'internal', @@ -11040,3 +11105,18 @@ { oid => '9038', descr => 'I/O', proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea', proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' }, + + +{ oid => '9040', descr => 'I/O', + proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary', + proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' }, +{ oid => '9041', descr => 'I/O', + proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' }, +{ oid => '9042', descr => 'I/O', + proname => 'brin_minmax_multi_summary_recv', provolatile => 's', + prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal', + prosrc => 'brin_minmax_multi_summary_recv' }, +{ oid => '9043', descr => 'I/O', + proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea', + proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 59da6a9804..af8d010c69 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -628,3 +628,10 @@ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out', typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, + +{ oid => '9039', + descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S', + typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out new file mode 100644 index 0000000000..966dc4aadc --- /dev/null +++ b/src/test/regress/expected/brin_multi.out @@ -0,0 +1,421 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +ERROR: value 15 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); +ERROR: value 257 out of bounds for option "values_per_range" +DETAIL: Valid values are between "16" and "256". +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; +RESET enable_seqscan; +RESET enable_bitmapscan; +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +VACUUM brintest_multi; -- force a summarization cycle in brinidx +UPDATE brintest_multi SET int8col = int8col * int4col; +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +ERROR: "brintest_multi" is not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + brin_summarize_new_values +--------------------------- + 0 +(1 row) + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +ERROR: block number out of range: -1 +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 0); + brin_desummarize_range +------------------------ + +(1 row) + +SELECT brin_desummarize_range('brinidx_multi', 100000000); + brin_desummarize_range +------------------------ + +(1 row) + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); + brin_summarize_range +---------------------- + 1 +(1 row) + +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); + brin_summarize_range +---------------------- + 0 +(1 row) + +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +ERROR: block number out of range: -1 +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); +ERROR: block number out of range: 4294967296 +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; + QUERY PLAN +-------------------------------------------------- + Bitmap Heap Scan on brin_test_multi + Recheck Cond: (a = 1) + -> Bitmap Index Scan on brin_test_multi_a_idx + Index Cond: (a = 1) +(4 rows) + +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; + QUERY PLAN +----------------------------- + Seq Scan on brin_test_multi + Filter: (b = 1) +(2 rows) + diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 72c7598c89..f855633634 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4986,12 +4986,13 @@ List of access methods (0 rows) \dAc brin pg*.oid* - List of operator classes - AM | Input type | Storage type | Operator class | Default? -------+------------+--------------+----------------+---------- - brin | oid | | oid_bloom_ops | no - brin | oid | | oid_minmax_ops | yes -(2 rows) + List of operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------------+---------- + brin | oid | | oid_bloom_ops | no + brin | oid | | oid_minmax_multi_ops | no + brin | oid | | oid_minmax_ops | yes +(3 rows) \dAf spgist List of operator families diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 97bf9797de..55a30a6b47 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); - oid | typname -------+----------------------- + oid | typname +------+------------------------------ 194 | pg_node_tree 3361 | pg_ndistinct 3402 | pg_dependencies 5017 | pg_mcv_list 9034 | pg_brin_bloom_summary -(5 rows) + 9039 | pg_brin_minmax_multi_summary +(6 rows) -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 69c39a1ca6..5050980cd4 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview # ---------- # Additional BRIN tests # ---------- -test: brin_bloom +test: brin_bloom brin_multi # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 6388bc7843..aa8bd4437e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -109,6 +109,7 @@ test: namespace test: prepared_xacts test: brin test: brin_bloom +test: brin_multi test: gin test: gist test: spgist diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql new file mode 100644 index 0000000000..9de6ddc6d7 --- /dev/null +++ b/src/test/regress/sql/brin_multi.sql @@ -0,0 +1,371 @@ +CREATE TABLE brintest_multi ( + int8col bigint, + int2col smallint, + int4col integer, + oidcol oid, + tidcol tid, + float4col real, + float8col double precision, + macaddrcol macaddr, + inetcol inet, + cidrcol cidr, + datecol date, + timecol time without time zone, + timestampcol timestamp without time zone, + timestamptzcol timestamp with time zone, + intervalcol interval, + timetzcol time with time zone, + numericcol numeric, + uuidcol uuid, + lsncol pg_lsn +) WITH (fillfactor=10); + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4/24' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20+02' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 100; + +-- throw in some NULL's and different values +INSERT INTO brintest_multi (inetcol, cidrcol) SELECT + inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous +FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; + +-- test minmax-multi specific index options +-- number of values must be >= 16 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 15) +); +-- number of values must be <= 256 +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops(values_per_range = 257) +); + +CREATE INDEX brinidx_multi ON brintest_multi USING brin ( + int8col int8_minmax_multi_ops, + int2col int2_minmax_multi_ops, + int4col int4_minmax_multi_ops, + oidcol oid_minmax_multi_ops, + tidcol tid_minmax_multi_ops, + float4col float4_minmax_multi_ops, + float8col float8_minmax_multi_ops, + macaddrcol macaddr_minmax_multi_ops, + inetcol inet_minmax_multi_ops, + cidrcol inet_minmax_multi_ops, + datecol date_minmax_multi_ops, + timecol time_minmax_multi_ops, + timestampcol timestamp_minmax_multi_ops, + timestamptzcol timestamptz_minmax_multi_ops, + intervalcol interval_minmax_multi_ops, + timetzcol timetz_minmax_multi_ops, + numericcol numeric_minmax_multi_ops, + uuidcol uuid_minmax_multi_ops, + lsncol pg_lsn_minmax_multi_ops +) with (pages_per_range = 1); + +CREATE TABLE brinopers_multi (colname name, typ text, + op text[], value text[], matches int[], + check (cardinality(op) = cardinality(value)), + check (cardinality(op) = cardinality(matches))); + +INSERT INTO brinopers_multi VALUES + ('int2col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int2col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int2', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int4', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1999}', + '{100, 100, 1, 100, 100}'), + ('int4col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 800, 1999, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('int8col', 'int2', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int4', + '{>, >=}', + '{0, 0}', + '{100, 100}'), + ('int8col', 'int8', + '{>, >=, =, <=, <}', + '{0, 0, 1257141600, 1428427143, 1428427143}', + '{100, 100, 1, 100, 100}'), + ('oidcol', 'oid', + '{>, >=, =, <=, <}', + '{0, 0, 8800, 9999, 9999}', + '{100, 100, 1, 100, 100}'), + ('tidcol', 'tid', + '{>, >=, =, <=, <}', + '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', + '{100, 100, 1, 100, 100}'), + ('float4col', 'float4', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float4col', 'float8', + '{>, >=, =, <=, <}', + '{0.0103093, 0.0103093, 1, 1, 1}', + '{100, 100, 4, 100, 96}'), + ('float8col', 'float4', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('float8col', 'float8', + '{>, >=, =, <=, <}', + '{0, 0, 0, 1.98, 1.98}', + '{99, 100, 1, 100, 100}'), + ('macaddrcol', 'macaddr', + '{>, >=, =, <=, <}', + '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', + '{99, 100, 2, 100, 100}'), + ('inetcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{1, 100, 100, 125, 125}'), + ('inetcol', 'cidr', + '{<, <=, >, >=}', + '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{100, 100, 125, 125}'), + ('cidrcol', 'inet', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('cidrcol', 'cidr', + '{=, <, <=, >, >=}', + '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}', + '{2, 100, 100, 125, 125}'), + ('datecol', 'date', + '{>, >=, =, <=, <}', + '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', + '{100, 100, 1, 100, 100}'), + ('timecol', 'time', + '{>, >=, =, <=, <}', + '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamp', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestampcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', + '{100, 100, 1, 100, 100}'), + ('timestamptzcol', 'timestamptz', + '{>, >=, =, <=, <}', + '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', + '{100, 100, 1, 100, 100}'), + ('intervalcol', 'interval', + '{>, >=, =, <=, <}', + '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', + '{100, 100, 1, 100, 100}'), + ('timetzcol', 'timetz', + '{>, >=, =, <=, <}', + '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', + '{99, 100, 2, 100, 100}'), + ('numericcol', 'numeric', + '{>, >=, =, <=, <}', + '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', + '{100, 100, 1, 100, 100}'), + ('uuidcol', 'uuid', + '{>, >=, =, <=, <}', + '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', + '{100, 100, 1, 100, 100}'), + ('lsncol', 'pg_lsn', + '{>, >=, =, <=, <, IS, IS NOT}', + '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', + '{100, 100, 1, 100, 100, 25, 100}'); + +DO $x$ +DECLARE + r record; + r2 record; + cond text; + idx_ctids tid[]; + ss_ctids tid[]; + count int; + plan_ok bool; + plan_line text; +BEGIN + FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP + + -- prepare the condition + IF r.value IS NULL THEN + cond := format('%I %s %L', r.colname, r.oper, r.value); + ELSE + cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); + END IF; + + -- run the query using the brin index + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get bitmap indexscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO idx_ctids; + + -- run the query using a seqscan + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + + plan_ok := false; + FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP + IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN + plan_ok := true; + END IF; + END LOOP; + IF NOT plan_ok THEN + RAISE WARNING 'did not get seqscan plan for %', r; + END IF; + + EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) + INTO ss_ctids; + + -- make sure both return the same results + count := array_length(idx_ctids, 1); + + IF NOT (count = array_length(ss_ctids, 1) AND + idx_ctids @> ss_ctids AND + idx_ctids <@ ss_ctids) THEN + -- report the results of each scan to make the differences obvious + RAISE WARNING 'something not right in %: count %', r, count; + SET enable_seqscan = 1; + SET enable_bitmapscan = 0; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'seqscan: %', r2; + END LOOP; + + SET enable_seqscan = 0; + SET enable_bitmapscan = 1; + FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP + RAISE NOTICE 'bitmapscan: %', r2; + END LOOP; + END IF; + + -- make sure we found expected number of matches + IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; + END LOOP; +END; +$x$; + +RESET enable_seqscan; +RESET enable_bitmapscan; + +INSERT INTO brintest_multi SELECT + 142857 * tenthous, + thousand, + twothousand, + unique1::oid, + format('(%s,%s)', tenthous, twenty)::tid, + (four + 1.0)/(hundred+1), + odd::float8 / (tenthous + 1), + format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, + inet '10.2.3.4' + tenthous, + cidr '10.2.3/24' + tenthous, + date '1995-08-15' + tenthous, + time '01:20:30' + thousand * interval '18.5 second', + timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', + timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', + justify_days(justify_hours(tenthous * interval '12 minutes')), + timetz '01:30:20' + hundred * interval '15 seconds', + tenthous::numeric(36,30) * fivethous * even / (hundred + 1), + format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, + format('%s/%s%s', odd, even, tenthous)::pg_lsn +FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; + +SELECT brin_desummarize_range('brinidx_multi', 0); +VACUUM brintest_multi; -- force a summarization cycle in brinidx + +UPDATE brintest_multi SET int8col = int8col * int4col; + +-- Tests for brin_summarize_new_values +SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index +SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index +SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected + +-- Tests for brin_desummarize_range +SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 0); +SELECT brin_desummarize_range('brinidx_multi', 100000000); + +-- Test brin_summarize_range +CREATE TABLE brin_summarize_multi ( + value int +) WITH (fillfactor=10, autovacuum_enabled=false); +CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); +-- Fill a few pages +DO $$ +DECLARE curtid tid; +BEGIN + LOOP + INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid; + EXIT WHEN curtid > tid '(2, 0)'; + END LOOP; +END; +$$; + +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 0); +-- nothing: already summarized +SELECT brin_summarize_range('brin_summarize_multi_idx', 1); +-- summarize one range +SELECT brin_summarize_range('brin_summarize_multi_idx', 2); +-- nothing: page doesn't exist in table +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); +-- invalid block number values +SELECT brin_summarize_range('brin_summarize_multi_idx', -1); +SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); + + +-- test brin cost estimates behave sanely based on correlation of values +CREATE TABLE brin_test_multi (a INT, b INT); +INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); +CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); +CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); +VACUUM ANALYZE brin_test_multi; + +-- Ensure brin index is used when columns are perfectly correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; +-- Ensure brin index is not used when values are not correlated +EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; -- 2.26.2
0008-Ignore-correlation-for-new-BRIN-opclasses-20201108.patch (text/x-patch, 4.2K) download From dabbb0c7297b99ecee66275922fd84e2a163cb26 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Sat, 12 Sep 2020 15:07:59 +0200 Subject: [PATCH 8/8] Ignore correlation for new BRIN opclasses The new BRIN opclasses (bloom and minmax-multi) are less sensitive to poorly correlated data, so just assume the data is perfectly correlated during costing. Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com --- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_minmax_multi.c | 1 + src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++- src/include/access/brin_internal.h | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index d5581abf7b..06dc606cbd 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -795,6 +795,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(BloomOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (BloomOpaque *) diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index f9a7ae6608..4f6262f241 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS) result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) + sizeof(MinmaxMultiOpaque)); + result->oi_ignore_correlation = true; result->oi_nstored = 1; result->oi_regular_nulls = true; result->oi_opaque = (MinmaxMultiOpaque *) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index bec357fcef..d84bf4726c 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -98,6 +98,7 @@ #include <math.h> #include "access/brin.h" +#include "access/brin_internal.h" #include "access/brin_page.h" #include "access/gin.h" #include "access/table.h" @@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double minimalRanges; double estimatedRanges; double selec; - Relation indexRel; + Relation indexRel = NULL; + TupleDesc tupdesc = NULL; ListCell *l; VariableStatData vardata; @@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, */ indexRel = index_open(index->indexoid, NoLock); brinGetStats(indexRel, &statsData); + tupdesc = RelationGetDescr(indexRel); index_close(indexRel, NoLock); /* work out the actual number of ranges in the index */ @@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, { IndexClause *iclause = lfirst_node(IndexClause, l); AttrNumber attnum = index->indexkeys[iclause->indexcol]; + FmgrInfo *opcInfoFn; + BrinOpcInfo *opcInfo; + Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol); + bool ignore_correlation; + + opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO); + + opcInfo = (BrinOpcInfo *) + DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid)); + + ignore_correlation = opcInfo->oi_ignore_correlation; /* attempt to lookup stats in relation for this index column */ if (attnum != 0) @@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, if (sslot.nnumbers > 0) varCorrelation = Abs(sslot.numbers[0]); + if (ignore_correlation) + varCorrelation = 1.0; + if (varCorrelation > *indexCorrelation) *indexCorrelation = varCorrelation; diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index ee4d0706df..67aea62a02 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -30,6 +30,9 @@ typedef struct BrinOpcInfo /* Regular processing of NULLs in BrinValues? */ bool oi_regular_nulls; + /* Ignore correlation during cost estimation */ + bool oi_ignore_correlation; + /* Opaque pointer for the opclass' private use */ void *oi_opaque; -- 2.26.2
^ permalink raw reply [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2020-11-09 14:29                                                                                                                                                                 ` John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-11-09 14:29 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Sat, Nov 7, 2020 at 4:38 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:

> Overall, I think there's very little difference, particularly in the
> "match" cases when we're searching for a value that we know is in the
> table. The one-hash variant seems to perform a bit better, but the
> difference is fairly small.
>
> In the "mismatch" cases (searching for value that is not in the table)
> the differences are more significant, but it might be noise. It does
> seem much more "green" than "red", i.e. the one-hash variant seems to be
> faster (although this does depend on the values for formatting).
>
> To sum this up, I think the one-hash approach seems interesting. It's
> not going to give us huge speedups because we're only hashing int32
> values anyway (not the source data), but it's worth exploring.

Thanks for testing! It seems you tested against the version with two
moduli, and not the alternative discussed in

https://www.postgresql.org/message-id/20200918222702.omsieaphfj3ctqg3%40development

which would in fact be rehashing the 32 bit values. I think that would be
the way to go if we don't use the one-hashing approach.

> a) set_bloom_partitions does this:
>
>     while (primes[pidx + nhashes - 1] <= target && primes[pidx] > 0)
>        pidx++;
>
> which is broken, because the second part of the condition only checks
> the current index - we may end up using nhashes primes after that, and
> some of them may be 0. So this needs to be:
>
>     while (primes[pidx + nhashes - 1] <= target &&
>            primes[pidx + nhashes] > 0)
>        pidx++;

Good catch.

> b) set_bloom_partitions does this to generate primes:
>
>     /*
>      * Increase the limit to ensure we have some primes higher than
>      * the target partition length. The 100 value is arbitrary, but
>      * should be well over what we need.
>      */
>     primes = generate_primes(target_partlen + 100);
>
> It's not clear to me why 100 is sufficient, particularly for large page
> sizes. AFAIK the primes get more and more sparse, so how does this
> guarantee we'll get enough "sufficiently large" primes?

This value is not rigorous and should be improved, but I started with that
by looking at the table in section 3 in

https://primes.utm.edu/notes/gaps.html

I see two ways to make a stronger guarantee:

1. Take the average gap between primes near n, which is log(n), and
multiply that by BLOOM_MAX_NUM_PARTITIONS. Adding that to the target seems
a better heuristic than a constant, and is still simple to calculate.

With the pathological example you gave of n=575104, k=3 (target_partlen =
191701), the number to add is log(191701) * 10 = 122.  By the table
referenced above, the largest prime gap under 360653 is 95, so we're
guaranteed to find at least one prime in the space of 122 above the target.
That will likely be enough to find the closest-to-target filter size for
k=3. Even if it weren't, nbits is so large that the relative difference is
tiny. I'd say a heuristic like this is most likely to be off precisely when
it matters the least. At this size, even if we find zero primes above our
target, the relative filter size is close to

(575104 - 3 * 95) / 575104 = 0.9995

For a more realistic bad-case target partition length, log(1327) * 10 = 72.
There are 33 composites after 1327, the largest such gap below 9551. That
would give five primes larger than the target
1361   1367   1373   1381   1399

which is more than enough for k<=10:

1297 +  1301  + 1303  + 1307  + 1319  + 1321  + 1327  + 1361 + 1367 + 1373
= 13276

2. Use a "segmented range" algorithm for the sieve and iterate until we get
k*2 primes, half below and half above the target. This would be an absolute
guarantee, but also more code, so I'm inclined against that.

> c) generate_primes uses uint16 to store the primes, so it can only
> generate primes up to 32768. That's (probably) enough for 8kB pages, but
> for 32kB pages it's clearly insufficient.

Okay.

> As for the original question how expensive this naive sieve is, I
> haven't been able to measure any significant timings. The logging aroung
> generate_primes usually looks like this:
>
> 2020-11-07 20:36:10.614 CET [232789] LOG:  generating primes nbits
> 575104 nhashes 3 target_partlen 191701
> 2020-11-07 20:36:10.614 CET [232789] LOG:  primes generated
>
> So it takes 0.000 second for this extreme page size. I don't think we
> need to invent anything more elaborate.

Okay, good to know. If we were concerned about memory, we could have it
check only odd numbers. That's a common feature of sieves, but also makes
the code a bit harder to understand if you haven't seen it before.

Also to fill in something I left for later, the reference for this

/* upper bound of number of primes below limit */
/* WIP: reference for this number */
int numprimes = 1.26 * limit / log(limit);

is

Rosser, J. Barkley; Schoenfeld, Lowell (1962). "Approximate formulas for
some functions of prime numbers". Illinois J. Math. 6: 64–94.
doi:10.1215/ijm/1255631807

More precisely, it's 30*log(113)/113 rounded up.

--
John Naylor
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
@ 2020-11-09 17:39                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2020-11-09 17:39 UTC (permalink / raw)
  To: John Naylor <john.naylor@enterprisedb.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>



On 11/9/20 3:29 PM, John Naylor wrote:
> On Sat, Nov 7, 2020 at 4:38 PM Tomas Vondra
> <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>>
> wrote:
> 
>> Overall, I think there's very little difference, particularly in the
>> "match" cases when we're searching for a value that we know is in the
>> table. The one-hash variant seems to perform a bit better, but the
>> difference is fairly small.
>>
>> In the "mismatch" cases (searching for value that is not in the table)
>> the differences are more significant, but it might be noise. It does
>> seem much more "green" than "red", i.e. the one-hash variant seems to be
>> faster (although this does depend on the values for formatting).
>>
>> To sum this up, I think the one-hash approach seems interesting. It's
>> not going to give us huge speedups because we're only hashing int32
>> values anyway (not the source data), but it's worth exploring.
> 
> Thanks for testing! It seems you tested against the version with two
> moduli, and not the alternative discussed in
> 
> https://www.postgresql.org/message-id/20200918222702.omsieaphfj3ctqg3%40development
> <https://www.postgresql.org/message-id/20200918222702.omsieaphfj3ctqg3%40development;
> 
> which would in fact be rehashing the 32 bit values. I think that would
> be the way to go if we don't use the one-hashing approach.
> 

Yeah. I forgot about this detail, and I may try again with the two-hash
variant, but I wonder how much difference would it make, considering the
results match the expected results (that is, the scan fraction" results
for fill_factor=100 match the target fpr almost perfectly).

I think there's a possibly-more important omission in the testing - I
forgot about the "sort mode" used initially, when the filter keeps the
actual hash values and only switches to hashing later. I wonder if that
plays role for some of the cases.

I'll investigate this a bit in the next round of tests.


>> a) set_bloom_partitions does this:
>>
>>     while (primes[pidx + nhashes - 1] <= target && primes[pidx] > 0)
>>        pidx++;
>>
>> which is broken, because the second part of the condition only checks
>> the current index - we may end up using nhashes primes after that, and
>> some of them may be 0. So this needs to be:
>>
>>     while (primes[pidx + nhashes - 1] <= target &&
>>            primes[pidx + nhashes] > 0)
>>        pidx++;
> 
> Good catch.
> 
>> b) set_bloom_partitions does this to generate primes:
>>
>>     /*
>>      * Increase the limit to ensure we have some primes higher than
>>      * the target partition length. The 100 value is arbitrary, but
>>      * should be well over what we need.
>>      */
>>     primes = generate_primes(target_partlen + 100);
>>
>> It's not clear to me why 100 is sufficient, particularly for large page
>> sizes. AFAIK the primes get more and more sparse, so how does this
>> guarantee we'll get enough "sufficiently large" primes?
> 
> This value is not rigorous and should be improved, but I started with
> that by looking at the table in section 3 in
> 
> https://primes.utm.edu/notes/gaps.html
> <https://primes.utm.edu/notes/gaps.html;
> 
> I see two ways to make a stronger guarantee:
> 
> 1. Take the average gap between primes near n, which is log(n), and
> multiply that by BLOOM_MAX_NUM_PARTITIONS. Adding that to the target
> seems a better heuristic than a constant, and is still simple to calculate.
> 
> With the pathological example you gave of n=575104, k=3 (target_partlen
> = 191701), the number to add is log(191701) * 10 = 122.  By the table
> referenced above, the largest prime gap under 360653 is 95, so we're
> guaranteed to find at least one prime in the space of 122 above the
> target. That will likely be enough to find the closest-to-target filter
> size for k=3. Even if it weren't, nbits is so large that the relative
> difference is tiny. I'd say a heuristic like this is most likely to be
> off precisely when it matters the least. At this size, even if we find
> zero primes above our target, the relative filter size is close to 
> 
> (575104 - 3 * 95) / 575104 = 0.9995
> 
> For a more realistic bad-case target partition length, log(1327) * 10 =
> 72. There are 33 composites after 1327, the largest such gap below 9551.
> That would give five primes larger than the target
> 1361   1367   1373   1381   1399
> 
> which is more than enough for k<=10:
> 
> 1297 +  1301  + 1303  + 1307  + 1319  + 1321  + 1327  + 1361 + 1367 +
> 1373 = 13276
> 
> 2. Use a "segmented range" algorithm for the sieve and iterate until we
> get k*2 primes, half below and half above the target. This would be an
> absolute guarantee, but also more code, so I'm inclined against that.
> 

Thanks, that makes sense.

While investigating the failures, I've tried increasing the values a
lot, without observing any measurable increase in runtime. IIRC I've
even used (10 * target_partlen) or something like that. That tells me
it's not very sensitive part of the code, so I'd suggest to simply use
something that we know is large enough to be safe.

For example, the largest bloom filter we can have is 32kB, i.e. 262kb,
at which point the largest gap is less than 95 (per the gap table). And
we may use up to BLOOM_MAX_NUM_PARTITIONS, so let's just use

    BLOOM_MAX_NUM_PARTITIONS * 100

on the basis that we may need BLOOM_MAX_NUM_PARTITIONS partitions
before/after the target. We could consider the actual target being lower
(essentially 1/npartions of the nbits) which decreases the maximum gap,
but I don't think that's the extra complexity here.


FWIW I wonder if we should do something about bloom filters that we know
can get larger than page size. In the example I used, we know that
nbits=575104 is larger than page, so as the filter gets more full (and
thus more random and less compressible) it won't possibly fit. Maybe we
should reject that right away, instead of "delaying it" until later, on
the basis that it's easier to fix at CREATE INDEX time (compared to when
inserts/updates start failing at a random time).

The problem with this is of course that if the index is multi-column,
this may not be strict enough (i.e. each filter would fit independently,
but the whole index row is too large). But it's probably better to do at
least something, and maybe improve that later with some whole-row check.


>> c) generate_primes uses uint16 to store the primes, so it can only
>> generate primes up to 32768. That's (probably) enough for 8kB pages, but
>> for 32kB pages it's clearly insufficient.
> 
> Okay.
> 
>> As for the original question how expensive this naive sieve is, I
>> haven't been able to measure any significant timings. The logging aroung
>> generate_primes usually looks like this:
>>
>> 2020-11-07 20:36:10.614 CET [232789] LOG:  generating primes nbits
>> 575104 nhashes 3 target_partlen 191701
>> 2020-11-07 20:36:10.614 CET [232789] LOG:  primes generated
>>
>> So it takes 0.000 second for this extreme page size. I don't think we
>> need to invent anything more elaborate.
> 
> Okay, good to know. If we were concerned about memory, we could have it
> check only odd numbers. That's a common feature of sieves, but also
> makes the code a bit harder to understand if you haven't seen it before.
> 

IMO if we were concerned about memory we'd use Bitmapset instead of an
array of bools. That's 1:8 compression, not just 1:2.

> Also to fill in something I left for later, the reference for this
> 
> /* upper bound of number of primes below limit */
> /* WIP: reference for this number */
> int numprimes = 1.26 * limit / log(limit);
> 
> is
> 
> Rosser, J. Barkley; Schoenfeld, Lowell (1962). "Approximate formulas for
> some functions of prime numbers". Illinois J. Math. 6: 64–94.
> doi:10.1215/ijm/1255631807
> 
> More precisely, it's 30*log(113)/113 rounded up.
> 

Thanks, I was wondering where that came from.


-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2020-11-09 19:20                                                                                                                                                                     ` John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2020-11-09 19:20 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Nov 9, 2020 at 1:39 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
>
>
> While investigating the failures, I've tried increasing the values a
> lot, without observing any measurable increase in runtime. IIRC I've
> even used (10 * target_partlen) or something like that. That tells me
> it's not very sensitive part of the code, so I'd suggest to simply use
> something that we know is large enough to be safe.

Okay, then it's not worth being clever.

> For example, the largest bloom filter we can have is 32kB, i.e. 262kb,
> at which point the largest gap is less than 95 (per the gap table). And
> we may use up to BLOOM_MAX_NUM_PARTITIONS, so let's just use
>     BLOOM_MAX_NUM_PARTITIONS * 100

Sure.

> FWIW I wonder if we should do something about bloom filters that we know
> can get larger than page size. In the example I used, we know that
> nbits=575104 is larger than page, so as the filter gets more full (and
> thus more random and less compressible) it won't possibly fit. Maybe we
> should reject that right away, instead of "delaying it" until later, on
> the basis that it's easier to fix at CREATE INDEX time (compared to when
> inserts/updates start failing at a random time).

Yeah, I'd be inclined to reject that right away.

> The problem with this is of course that if the index is multi-column,
> this may not be strict enough (i.e. each filter would fit independently,
> but the whole index row is too large). But it's probably better to do at
> least something, and maybe improve that later with some whole-row check.

A whole-row check would be nice, but I don't know how hard that would be.

As a Devil's advocate proposal, how awful would it be to not allow
multicolumn brin-bloom indexes?

--
John Naylor
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-02 06:42                                                                                                                                                                         ` Thomas Munro <thomas.munro@gmail.com>
  2021-01-02 22:56                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Thomas Munro @ 2021-01-02 06:42 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: John Naylor <john.naylor@enterprisedb.com>; Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Sun, Dec 20, 2020 at 1:16 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
> Attached is an updated version of the patch series, rebased on current
> master, and results for benchmark comparing the various bloom variants.

Perhaps src/include/utils/inet.h needs to include <sys/socket.h>,
because FreeBSD says:

brin_minmax_multi.c:1693:24: error: use of undeclared identifier 'AF_INET'
if (ip_family(ipa) == PGSQL_AF_INET)
^
../../../../src/include/utils/inet.h:39:24: note: expanded from macro
'PGSQL_AF_INET'
#define PGSQL_AF_INET (AF_INET + 0)
^





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-02 06:42                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Thomas Munro <thomas.munro@gmail.com>
@ 2021-01-02 22:56                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 0 replies; 177+ messages in thread

From: Tomas Vondra @ 2021-01-02 22:56 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: John Naylor <john.naylor@enterprisedb.com>; Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On 1/2/21 7:42 AM, Thomas Munro wrote:
> On Sun, Dec 20, 2020 at 1:16 PM Tomas Vondra
> <tomas.vondra@enterprisedb.com> wrote:
>> Attached is an updated version of the patch series, rebased on current
>> master, and results for benchmark comparing the various bloom variants.
> 
> Perhaps src/include/utils/inet.h needs to include <sys/socket.h>,
> because FreeBSD says:
> 
> brin_minmax_multi.c:1693:24: error: use of undeclared identifier 'AF_INET'
> if (ip_family(ipa) == PGSQL_AF_INET)
> ^
> ../../../../src/include/utils/inet.h:39:24: note: expanded from macro
> 'PGSQL_AF_INET'
> #define PGSQL_AF_INET (AF_INET + 0)
> ^

Not sure. The other files using PGSQL_AF_INET just include sys/socket.h
directly, so maybe this should just do the same thing ...


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-12 17:28                                                                                                                                                                         ` John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-13 01:09                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 2 replies; 177+ messages in thread

From: John Naylor @ 2021-01-12 17:28 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Sat, Dec 19, 2020 at 8:15 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
> [12-20 version]

Hi Tomas,

The measurements look good. In case it fell through the cracks, my earlier
review comments for Bloom BRIN indexes regarding minor details don't seem
to have been addressed in this version. I'll point to earlier discussion
for convenience:

https://www.postgresql.org/message-id/CACPNZCt%3Dx-fOL0CUJbjR3BFXKgcd9HMPaRUVY9cwRe58hmd8Xg%40mail.g...

https://www.postgresql.org/message-id/CACPNZCuqpkCGt8%3DcywAk1kPu0OoV_TjPXeV-J639ABQWyViyug%40mail.g...

> The improvements are fairly minor:
>
> 1) Rejecting bloom filters that are clearly too large (larger than page)
> early. This is imperfect, as it works for individual index keys, not the
> whole row. But per discussion it seems useful.

I think this is good enough.

> So based on this I'm tempted to just use the version with two hashes, as
> implemented in 0005. It's much simpler than the partitioning scheme,
> does not need any of the logic to generate primes etc.

Sounds like the best engineering decision.

Circling back to multi-minmax build times, I ran a couple quick tests on
bigger hardware, and found that not only is multi-minmax slower than
minmax, which is to be expected, but also slower than btree. (unlogged
table ~12GB in size, maintenance_work_mem = 1GB, median of three runs)

btree          38.3s
minmax         26.2s
multi-minmax  110s

Since btree indexes are much larger, I imagine something algorithmic is
involved. Is it worth digging further to see if some code path is taking
more time than we would expect?

--
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
@ 2021-01-12 17:42                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2021-01-12 17:42 UTC (permalink / raw)
  To: John Naylor <john.naylor@enterprisedb.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>



On 1/12/21 6:28 PM, John Naylor wrote:
> On Sat, Dec 19, 2020 at 8:15 PM Tomas Vondra 
> <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>> 
> wrote:
>  > [12-20 version]
> 
> Hi Tomas,
> 
> The measurements look good. In case it fell through the cracks, my 
> earlier review comments for Bloom BRIN indexes regarding minor details 
> don't seem to have been addressed in this version. I'll point to earlier 
> discussion for convenience:
> 
> https://www.postgresql.org/message-id/CACPNZCt%3Dx-fOL0CUJbjR3BFXKgcd9HMPaRUVY9cwRe58hmd8Xg%40mail.g... 
> <https://www.postgresql.org/message-id/CACPNZCt%3Dx-fOL0CUJbjR3BFXKgcd9HMPaRUVY9cwRe58hmd8Xg%40mail.g...;
> 
> https://www.postgresql.org/message-id/CACPNZCuqpkCGt8%3DcywAk1kPu0OoV_TjPXeV-J639ABQWyViyug%40mail.g... 
> <https://www.postgresql.org/message-id/CACPNZCuqpkCGt8%3DcywAk1kPu0OoV_TjPXeV-J639ABQWyViyug%40mail.g...;
> 

Whooops :-( I'll go through those again, thanks for reminding me.

>  > The improvements are fairly minor:
>  >
>  > 1) Rejecting bloom filters that are clearly too large (larger than page)
>  > early. This is imperfect, as it works for individual index keys, not the
>  > whole row. But per discussion it seems useful.
> 
> I think this is good enough.
> 
>  > So based on this I'm tempted to just use the version with two hashes, as
>  > implemented in 0005. It's much simpler than the partitioning scheme,
>  > does not need any of the logic to generate primes etc.
> 
> Sounds like the best engineering decision.
> 
> Circling back to multi-minmax build times, I ran a couple quick tests on 
> bigger hardware, and found that not only is multi-minmax slower than 
> minmax, which is to be expected, but also slower than btree. (unlogged 
> table ~12GB in size, maintenance_work_mem = 1GB, median of three runs)
> 
> btree          38.3s
> minmax         26.2s
> multi-minmax  110s
> 
> Since btree indexes are much larger, I imagine something algorithmic is 
> involved. Is it worth digging further to see if some code path is taking 
> more time than we would expect?
> 

I suspect it'd due to minmax having to decide which "ranges" to merge, 
which requires repeated sorting, etc. I certainly don't dare to claim 
the current algorithm is perfect. I wouldn't have expected such big 
difference, though - so definitely worth investigating.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-19 20:44                                                                                                                                                                             ` John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2021-01-19 20:44 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Jan 12, 2021 at 1:42 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
> I suspect it'd due to minmax having to decide which "ranges" to merge,
> which requires repeated sorting, etc. I certainly don't dare to claim
> the current algorithm is perfect. I wouldn't have expected such big
> difference, though - so definitely worth investigating.

It seems that monotonically increasing (or decreasing) values in a table
are a worst case scenario for multi-minmax indexes, or basically, unique
values within a range. I'm guessing it's because it requires many passes
to fit all the values into a limited number of ranges. I tried using
smaller pages_per_range numbers, 32 and 8, and that didn't help.

Now, with a different data distribution, using only 10 values that repeat
over and over, the results are much more sympathetic to multi-minmax:

insert into iot (num, create_dt)
select random(), '2020-01-01 0:00'::timestamptz + (x % 10 || '
seconds')::interval
from generate_series(1,5*365*24*60*60) x;

create index cd_single on iot using brin(create_dt);
27.2s

create index cd_multi on iot using brin(create_dt
timestamptz_minmax_multi_ops);
30.4s

create index cd_bt on iot using btree(create_dt);
61.8s

Circling back to the monotonic case, I tried running a simple perf record
on a backend creating a multi-minmax index on a timestamptz column and
these were the highest non-kernel calls:
+   21.98%    21.91%  postgres         postgres            [.]
FunctionCall2Coll
+    9.31%     9.29%  postgres         postgres            [.]
compare_combine_ranges
+    8.60%     8.58%  postgres         postgres            [.] qsort_arg
+    5.68%     5.66%  postgres         postgres            [.]
brin_minmax_multi_add_value
+    5.63%     5.60%  postgres         postgres            [.] timestamp_lt
+    4.73%     4.71%  postgres         postgres            [.]
reduce_combine_ranges
+    3.80%     0.00%  postgres         [unknown]           [.]
0x0320016800040000
+    3.51%     3.50%  postgres         postgres            [.] timestamp_eq

There's no one place that's pathological enough to explain the 4x slowness
over traditional BRIN and nearly 3x slowness over btree when using a large
number of unique values per range, so making progress here would have to
involve a more holistic approach.

--
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
@ 2021-01-20 00:07                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2021-01-20 00:07 UTC (permalink / raw)
  To: John Naylor <john.naylor@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 1/19/21 9:44 PM, John Naylor wrote:
> On Tue, Jan 12, 2021 at 1:42 PM Tomas Vondra 
> <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>> 
> wrote:
>  > I suspect it'd due to minmax having to decide which "ranges" to merge,
>  > which requires repeated sorting, etc. I certainly don't dare to claim
>  > the current algorithm is perfect. I wouldn't have expected such big
>  > difference, though - so definitely worth investigating.
> 
> It seems that monotonically increasing (or decreasing) values in a table 
> are a worst case scenario for multi-minmax indexes, or basically, unique 
> values within a range. I'm guessing it's because it requires many passes 
> to fit all the values into a limited number of ranges. I tried using 
> smaller pages_per_range numbers, 32 and 8, and that didn't help.
> 
> Now, with a different data distribution, using only 10 values that 
> repeat over and over, the results are muchs more sympathetic to multi-minmax:
> 
> insert into iot (num, create_dt)
> select random(), '2020-01-01 0:00'::timestamptz + (x % 10 || ' 
> seconds')::interval
> from generate_series(1,5*365*24*60*60) x;
> 
> create index cd_single on iot using brin(create_dt);
> 27.2s
> 
> create index cd_multi on iot using brin(create_dt 
> timestamptz_minmax_multi_ops);
> 30.4s
> 
> create index cd_bt on iot using btree(create_dt);
> 61.8s
> 
> Circling back to the monotonic case, I tried running a simple perf 
> record on a backend creating a multi-minmax index on a timestamptz 
> column and these were the highest non-kernel calls:
> +   21.98%    21.91%  postgres         postgres            [.] 
> FunctionCall2Coll
> +    9.31%     9.29%  postgres         postgres            [.] 
> compare_combine_ranges
> +    8.60%     8.58%  postgres         postgres            [.] qsort_arg
> +    5.68%     5.66%  postgres         postgres            [.] 
> brin_minmax_multi_add_value
> +    5.63%     5.60%  postgres         postgres            [.] timestamp_lt
> +    4.73%     4.71%  postgres         postgres            [.] 
> reduce_combine_ranges
> +    3.80%     0.00%  postgres         [unknown]           [.] 
> 0x0320016800040000
> +    3.51%     3.50%  postgres         postgres            [.] timestamp_eq
> 
> There's no one place that's pathological enough to explain the 4x 
> slowness over traditional BRIN and nearly 3x slowness over btree when 
> using a large number of unique values per range, so making progress here 
> would have to involve a more holistic approach.
> 

Yeah. This very much seems like the primary problem is in how we build 
the ranges incrementally - with monotonic sequences, we end up having to 
merge the ranges over and over again. I don't know what was the 
structure of the table, but I guess it was kinda narrow (very few 
columns), which exacerbates the problem further, because the number of 
rows per range will be way higher than in real-world.

I do think the solution to this might be to allow more values during 
batch index creation, and only "compress" to the requested number at the 
very end (when serializing to on-disk format).

There are a couple additional comments about possibly replacing 
sequential scan with a binary search, that could help a bit too.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-22 23:27                                                                                                                                                                                   ` John Naylor <john.naylor@enterprisedb.com>
  2021-01-23 02:59                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2021-01-22 23:27 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jan 21, 2021 at 9:06 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
> [wip optimizations]

> The pathological case (monotonic-asc) is now 4x faster, roughly equal to
> regular minmax index build. The opposite (monotonic-desc) is about 33%
> faster, roughly in line with btree.

Those numbers look good. I get similar results, shown below. I've read
0007-8 briefly but not in depth.

> There are a couple cases where it's actually a bit slower - those are
> the cases with very few distinct values per range. I believe this
> happens because in the batch mode the code does not check if the summary
> already contains this value, adds it to the buffer and the last step
> ends up being more expensive than this.

I think if it's worst case a bit faster than btree and best case a bit
slower than traditional minmax, that's acceptable.

> I believe there's some "compromise" between those two extremes, i.e. we
> should use buffer that is too small or too large, but something in
> between, so that the reduction happens once in a while but not too often
> (as with the original aggressive approach).

This sounds good also.

> FWIW, none of this is likely to be an issue in practice, because (a)
> tables usually don't have such strictly monotonic patterns, (b) people
> should stick to plain minmax for cases that do.

Still, it would be great if multi-minmax can be a drop in replacement. I
know there was a sticking point of a distance function not being available
on all types, but I wonder if that can be remedied or worked around somehow.

And (c) regular tables
> tend to have much wider rows, so there are fewer values per range (so
> that other stuff is likely more expensive than building BRIN).

True. I'm still puzzled that it didn't help to use 8 pages per range, but
it's moot now.


Here are some numbers (median of 3) with a similar scenario as before,
repeated here with some added details. I didn't bother with what you call
"unpatched":

               btree   minmax   multi
monotonic-asc  44.4    26.5     27.8
mono-del-ins   38.7    24.6     30.4
mono-10-asc    61.8    25.6     33.5


create unlogged table iot (
    id bigint generated by default as identity primary key,
    num double precision not null,
    create_dt timestamptz not null,
    stuff text generated always as (md5(id::text)) stored
)
with (fillfactor = 95);

-- monotonic-asc:

insert into iot (num, create_dt)
select random(), x
from generate_series(
  '2020-01-01 0:00'::timestamptz,
  '2020-01-01 0:00'::timestamptz +'5 years'::interval,
  '1 second'::interval) x;

-- mono-del-ins:
-- Here I deleted a few values from (likely) each page in the above table,
and reinserted values that shouldn't be in existing ranges:

delete from iot1
where num < 0.05
or num > 0.95;

vacuum iot1;

insert into iot (num, create_dt)
select random(), x
from generate_series(
  '2020-01-01 0:00'::timestamptz,
  '2020-02-01 23:59'::timestamptz,
  '1 second'::interval) x;

-- mono-10-asc

truncate table iot;

insert into iot (num, create_dt)
select random(), '2020-01-01 0:00'::timestamptz + (x % 10 || '
seconds')::interval
from generate_series(1,5*365*24*60*60) x;

--
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 23:27                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
@ 2021-01-23 02:59                                                                                                                                                                                     ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-26 18:52                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2021-01-23 02:59 UTC (permalink / raw)
  To: John Naylor <john.naylor@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>


On 1/23/21 12:27 AM, John Naylor wrote:
> On Thu, Jan 21, 2021 at 9:06 PM Tomas Vondra 
> <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>> 
> wrote:
>  > [wip optimizations]
> 
>  > The pathological case (monotonic-asc) is now 4x faster, roughly equal to
>  > regular minmax index build. The opposite (monotonic-desc) is about 33%
>  > faster, roughly in line with btree.
> 
> Those numbers look good. I get similar results, shown below. I've read 
> 0007-8 briefly but not in depth.
>  >  > There are a couple cases where it's actually a bit slower - those are
>  > the cases with very few distinct values per range. I believe this
>  > happens because in the batch mode the code does not check if the summary
>  > already contains this value, adds it to the buffer and the last step
>  > ends up being more expensive than this.
> 
> I think if it's worst case a bit faster than btree and best case a bit 
> slower than traditional minmax, that's acceptable.
> 
>  > I believe there's some "compromise" between those two extremes, i.e. we
>  > should use buffer that is too small or too large, but something in
>  > between, so that the reduction happens once in a while but not too often
>  > (as with the original aggressive approach).
> 
> This sounds good also.
> 

Yeah, I agree.

I think the reason why some of the cases got a bit slower is that in 
those cases the original approach (ranges being built fairly frequently, 
not just once at the end) we quickly built something that represented 
the whole range, so adding a new value was often no-op. The add_value 
callback found the range already "includes" the new value, etc.

With the batch mode, that's no longer true - we accumulate everything, 
so we have to sort it etc. Which I guess may be fairly expensive, thanks 
to calling comparator functions etc. I wonder if this could be optimized 
a bit, e.g. by first "deduplicating" the values using memcmp() or so.

But ultimately, I think the right solution will be to limit the buffer 
size to something like 10x the target, and roll with that. Typically, 
increasing the buffer size from e.g. 100B to 1000B brings much clearer 
improvement than increasing it from 1000B to 10000B. I'd bet this 
follows the pattern.

>  > FWIW, none of this is likely to be an issue in practice, because (a)
>  > tables usually don't have such strictly monotonic patterns, (b) people
>  > should stick to plain minmax for cases that do.
> 
> Still, it would be great if multi-minmax can be a drop in replacement. I 
> know there was a sticking point of a distance function not being 
> available on all types, but I wonder if that can be remedied or worked 
> around somehow.
> 

Hmm. I think Alvaro also mentioned he'd like to use this as a drop-in 
replacement for minmax (essentially, using these opclasses as the 
default ones, with the option to switch back to plain minmax). I'm not 
convinced we should do that - though. Imagine you have minmax indexes in 
your existing DB, it's working perfectly fine, and then we come and just 
silently change that during dump/restore. Is there some past example 
when we did something similar and it turned it to be OK?

As for the distance functions, I'm pretty sure there are data types 
without "natural" distance - like most strings, for example. We could 
probably invent something, but the question is how much we can rely on 
it working well enough in practice.

Of course, is minmax even the right index type for such data types? 
Strings are usually "labels" and not queried using range queries, 
although sometimes people encode stuff as strings (but then it's very 
unlikely we'll define the distance definition well). So maybe for those 
types a hash / bloom would be a better fit anyway.


But I do have an idea - maybe we can do without distances, in those 
cases. Essentially, the primary issue of minmax indexes are outliers, so 
what if we simply sort the values, keep one range in the middle and as 
many single points on each tail?

Imagine we have N values, and we want to represent this by K values. We 
simply sort the N values, keep (k-2)/2 values on each tail as outliers, 
and use 2 values for the values in between.

Example:

input: [1, 2, 100, 110, 111, ..., 120, , ..., 130, 201, 202]

Given k = 6, we would keep 2 values on tails, and range for the rest:

   [1, 2, (100, 130), 201, 202]

Of course, this does not optimize for the same thing as when we have 
distance - in that case we try to minimize the "covering" of the input 
data, something like

      sum(length(r) for r in ranges) / (max(ranges) - min(ranges))

But maybe it's good enough when there's no distance function ...


> And (c) regular tables
>  > tend to have much wider rows, so there are fewer values per range (so
>  > that other stuff is likely more expensive than building BRIN).
> 
> True. I'm still puzzled that it didn't help to use 8 pages per range, 
> but it's moot now.
> 

I'd bet that even with just 8 pages, there were quite a few values in 
the range - possibly hundreds per page. I haven't tried if the patches 
help with smaller ranges, so maybe we should check.

> 
> Here are some numbers (median of 3) with a similar scenario as before, 
> repeated here with some added details. I didn't bother with what you 
> call "unpatched":
> 
>                 btree   minmax   multi
> monotonic-asc  44.4    26.5     27.8
> mono-del-ins   38.7    24.6     30.4
> mono-10-asc    61.8    25.6     33.5
> 

Thanks. Those numbers seem reasonable.


-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 23:27                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-23 02:59                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-26 18:52                                                                                                                                                                                       ` John Naylor <john.naylor@enterprisedb.com>
  2021-01-26 22:59                                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: John Naylor @ 2021-01-26 18:52 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Jan 22, 2021 at 10:59 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
>
>
> On 1/23/21 12:27 AM, John Naylor wrote:

> > Still, it would be great if multi-minmax can be a drop in replacement. I
> > know there was a sticking point of a distance function not being
> > available on all types, but I wonder if that can be remedied or worked
> > around somehow.
> >
>
> Hmm. I think Alvaro also mentioned he'd like to use this as a drop-in
> replacement for minmax (essentially, using these opclasses as the
> default ones, with the option to switch back to plain minmax). I'm not
> convinced we should do that - though. Imagine you have minmax indexes in
> your existing DB, it's working perfectly fine, and then we come and just
> silently change that during dump/restore. Is there some past example
> when we did something similar and it turned it to be OK?

I was assuming pg_dump can be taught to insert explicit opclasses for
minmax indexes, so that upgrade would not cause surprises. If that's true,
only new indexes would have the different default opclass.

> As for the distance functions, I'm pretty sure there are data types
> without "natural" distance - like most strings, for example. We could
> probably invent something, but the question is how much we can rely on
> it working well enough in practice.
>
> Of course, is minmax even the right index type for such data types?
> Strings are usually "labels" and not queried using range queries,
> although sometimes people encode stuff as strings (but then it's very
> unlikely we'll define the distance definition well). So maybe for those
> types a hash / bloom would be a better fit anyway.

Right.

> But I do have an idea - maybe we can do without distances, in those
> cases. Essentially, the primary issue of minmax indexes are outliers, so
> what if we simply sort the values, keep one range in the middle and as
> many single points on each tail?

That's an interesting idea. I think it would be a nice bonus to try to do
something along these lines. On the other hand, I'm not the one
volunteering to do the work, and the patch is useful as is.

--
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 23:27                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-23 02:59                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-26 18:52                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
@ 2021-01-26 22:59                                                                                                                                                                                         ` Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-30 19:19                                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tomas Vondra @ 2021-01-26 22:59 UTC (permalink / raw)
  To: John Naylor <john.naylor@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>



On 1/26/21 7:52 PM, John Naylor wrote:
> On Fri, Jan 22, 2021 at 10:59 PM Tomas Vondra 
> <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>> 
> wrote:
>  >
>  >
>  > On 1/23/21 12:27 AM, John Naylor wrote:
> 
>  > > Still, it would be great if multi-minmax can be a drop in 
> replacement. I
>  > > know there was a sticking point of a distance function not being
>  > > available on all types, but I wonder if that can be remedied or worked
>  > > around somehow.
>  > >
>  >
>  > Hmm. I think Alvaro also mentioned he'd like to use this as a drop-in
>  > replacement for minmax (essentially, using these opclasses as the
>  > default ones, with the option to switch back to plain minmax). I'm not
>  > convinced we should do that - though. Imagine you have minmax indexes in
>  > your existing DB, it's working perfectly fine, and then we come and just
>  > silently change that during dump/restore. Is there some past example
>  > when we did something similar and it turned it to be OK?
> 
> I was assuming pg_dump can be taught to insert explicit opclasses for 
> minmax indexes, so that upgrade would not cause surprises. If that's 
> true, only new indexes would have the different default opclass.
> 

Maybe, I suppose we could do that. But I always found such changes 
happening silently in the background a bit suspicious, because it may be 
quite confusing. I certainly wouldn't expect such difference between 
creating a new index and index created by dump/restore. Did we do such 
changes in the past? That might be a precedent, but I don't recall any 
example ...

>  > As for the distance functions, I'm pretty sure there are data types
>  > without "natural" distance - like most strings, for example. We could
>  > probably invent something, but the question is how much we can rely on
>  > it working well enough in practice.
>  >
>  > Of course, is minmax even the right index type for such data types?
>  > Strings are usually "labels" and not queried using range queries,
>  > although sometimes people encode stuff as strings (but then it's very
>  > unlikely we'll define the distance definition well). So maybe for those
>  > types a hash / bloom would be a better fit anyway.
> 
> Right.
> 
>  > But I do have an idea - maybe we can do without distances, in those
>  > cases. Essentially, the primary issue of minmax indexes are outliers, so
>  > what if we simply sort the values, keep one range in the middle and as
>  > many single points on each tail?
> 
> That's an interesting idea. I think it would be a nice bonus to try to 
> do something along these lines. On the other hand, I'm not the one 
> volunteering to do the work, and the patch is useful as is.
> 

IMO it's fairly small amount of code, so I'll take a stab at in in the 
next version of the patch.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-12 17:42                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-19 20:44                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-20 00:07                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 01:05                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-22 23:27                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-23 02:59                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-26 18:52                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-26 22:59                                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-30 19:19                                                                                                                                                                                           ` John Naylor <john.naylor@enterprisedb.com>
  0 siblings, 0 replies; 177+ messages in thread

From: John Naylor @ 2021-01-30 19:19 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Jan 26, 2021 at 6:59 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:
>
>
>
> On 1/26/21 7:52 PM, John Naylor wrote:
> > On Fri, Jan 22, 2021 at 10:59 PM Tomas Vondra
> > <tomas.vondra@enterprisedb.com <mailto:tomas.vondra@enterprisedb.com>>
> > wrote:
> >  > Hmm. I think Alvaro also mentioned he'd like to use this as a drop-in
> >  > replacement for minmax (essentially, using these opclasses as the
> >  > default ones, with the option to switch back to plain minmax). I'm
not
> >  > convinced we should do that - though. Imagine you have minmax
indexes in
> >  > your existing DB, it's working perfectly fine, and then we come and
just
> >  > silently change that during dump/restore. Is there some past example
> >  > when we did something similar and it turned it to be OK?
> >
> > I was assuming pg_dump can be taught to insert explicit opclasses for
> > minmax indexes, so that upgrade would not cause surprises. If that's
> > true, only new indexes would have the different default opclass.
> >
>
> Maybe, I suppose we could do that. But I always found such changes
> happening silently in the background a bit suspicious, because it may be
> quite confusing. I certainly wouldn't expect such difference between
> creating a new index and index created by dump/restore. Did we do such
> changes in the past? That might be a precedent, but I don't recall any
> example ...

I couldn't think of a comparable example either. It comes down to
evaluating risk. On the one hand it's nice if users get an enhancement
without having to know about it, on the other hand if there is some kind of
noticeable regression, that's bad.

--
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-13 01:09                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-01-26 18:40                                                                                                                                                                             ` John Naylor <john.naylor@enterprisedb.com>
  2021-02-03 23:54                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 1 reply; 177+ messages in thread

From: John Naylor @ 2021-01-26 18:40 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi Tomas,
I took another look through the Bloom opclass portion (0004) with
sorted_mode omitted, and it looks good to me code-wise. I think this part
is close to commit-ready. I also did one more proofreading pass for
minor details.

+     rows per block). The default values is <literal>-0.1</literal>, and

+     greater than 0.0 and smaller than 1.0. The default values is 0.01,

s/values/value/

+ * bloom filter, we can easily and cheaply test wheter it contains values

s/wheter/whether/

+ * XXX We assume the bloom filters have the same parameters fow now. In the

s/fow/for/

+ * or null if it does not exists.

s/exists/exist/

+ * We do expect the bloom filter to eventually switch to hashing mode,
+ * and it's bound to be almost perfectly random, so not compressible.

Leftover from when it started out in sorted mode.

+ if ((m/8) > BLCKSZ)

It seems we need something more safe, to account for page header and tuple
header at least. As the comment before says, the filter will eventually not
be compressible. I remember we can't be exact here, with the possibility of
multiple columns, but we can leave a little slack space.

-- 
John Naylor
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-13 01:09                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-26 18:40                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-02-03 23:54                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-02-04 00:49                                                                                                                                                                                 ` Zhihong Yu <zyu@yugabyte.com>
  2021-02-04 00:59                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  1 sibling, 1 reply; 177+ messages in thread

From: Zhihong Yu @ 2021-02-04 00:49 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: John Naylor <john.naylor@enterprisedb.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,
For 0007-Remove-the-special-batch-mode-use-a-larger--20210203.patch :

+       /* same as preceding value, so store it */
+       if (compare_values(&range->values[start + i - 1],
+                          &range->values[start + i],
+                          (void *) &cxt) == 0)
+           continue;
+
+       range->values[start + n] = range->values[start + i];

It seems the comment doesn't match the code: the value is stored when
subsequent value is different from the previous.

For has_matching_range():
+       int     midpoint = (start + end) / 2;

I think the standard notion for midpoint is start + (end-start)/2.

+       /* this means we ran out of ranges in the last step */
+       if (start > end)
+           return false;

It seems the above should be ahead of computation of midpoint.

Similar comment for the code in AssertCheckRanges().

Cheers

On Wed, Feb 3, 2021 at 3:55 PM Tomas Vondra <tomas.vondra@enterprisedb.com>
wrote:

> Hi,
>
> Here's an updated and significantly improved version of the patch
> series, particularly the multi-minmax part. I've fixed a number of
> stupid bugs in that, discovered by either valgrind or stress-tests.
>
> I was surprised by some of the bugs, or rather that the existing
> regression tests failed to crash on them, so it's probably worth briefly
> discussing the details. There were two main classes of such bugs:
>
>
> 1) missing datumCopy
>
> AFAICS this happened because there were a couple missing datumCopy
> calls, and BRIN covers multiple pages, so with by-ref data types we
> added a pointer but the buffer might have gone away unexpectedly.
> Regular regression tests passed just fine, because brin_multi runs
> almost separately, so the chance of the buffer being evicted was low.
> Valgrind reported this (with a rather enigmatic message, as usual), and
> so did a simple stress-test creating many indexes concurrently. Anything
> causing aggressive eviction of buffer would do the trick, I think,
> triggering segfaults, asserts, etc.
>
>
> 2) bogus opclass definitions
>
> There were a couple opclasses referencing incorrect distance function,
> intended for a different data type. I was scratching my head WTH the
> regression tests pass, as there is a table to build multi-minmax index
> on all supported data types. The reason is pretty silly - the table is
> very small, just 100 rows, with very low fillfactor (so only couple
> values per page), and the index was created with pages_per_range=1. So
> the compaction was not triggered and the distance function was never
> actually called. I've decided to build the indexes on a larger data set
> first, to test this. But maybe this needs somewhat different approach.
>
>
> BLOOM
> -----
>
> The attached patch series addresses comments from the last review. As
> for the size limit, I've defined a new macro
>
>     #define BloomMaxFilterSize \
>         MAXALIGN_DOWN(BLCKSZ - \
>                       (MAXALIGN(SizeOfPageHeaderData + \
>                                 sizeof(ItemIdData)) + \
>                        MAXALIGN(sizeof(BrinSpecialSpace)) + \
>                        SizeOfBrinTuple))
>
> and use that to determine if the bloom filter is too large. IMO that's
> close enough, considering that this is a best-effort check anyway (due
> to not being able to consider multi-column indexes).
>
>
> MINMAX-MULTI
> ------------
>
> As mentioned, there's a lot of fixes and improvements in this part, but
> the basic principle is still the same. I've kept it split into three
> parts with different approaches to building, so that it's possible to do
> benchmarks and comparisons, and pick the best one.
>
> a) 0005 - Aggressively compacts the summary, by always keeping it within
> the limit defined by values_per_range. So if the range contains more
> values, this may trigger compaction very often in some cases (e.g. for
> monotonic series).
>
> One drawback is that the more often the compactions happen, the less
> optimal the result is - the algorithm is kinda greedy, picking something
> like local optimums in each step.
>
> b) 0006 - Batch build, exactly the opposite of 0005. Accumulates all
> values in a buffer, then does a single round of compaction at the very
> end. This obviously doesn't have the "greediness" issues, but it may
> consume quite a bit of memory for some data types and/or indexes with
> large BRIN ranges.
>
> c) 0007 - A hybrid approach, using a buffer that is multiple of the
> user-specified value, with some safety min/max limits. IMO this is what
> we should use, although perhaps with some tuning of the exact limits.
>
>
> Attached is a spreadsheet with benchmark results for each of those three
> approaches, on different data types (byval/byref), data set types, index
> parameters (pages/values per range) etc. I think 0007 is a reasonable
> compromise overall, with performance somewhere in betwen 0005 and 0006.
> Of course, there are cases where it's somewhat slow, e.g. for data types
> with expensive comparisons and data sets forcing frequent compactions,
> in which case it's ~10x slower compared to regular minmax (in most cases
> it's ~1.5x). Compared to btree, it's usually much faster - ~2-3x as fast
> (except for some extreme cases, of course).
>
>
> As for the opclasses for indexes without "natural" distance function,
> implemented in 0008, I think we should drop it. In theory it works, but
> I'm far from convinced it's actually useful in practice. Essentially, if
> you have a data type with ordering but without a meaningful concept of a
> distance, it's hard to say what is an outlier. I'd bet most of those
> data types are used as "labels" where even the ordering is kinda
> useless, i.e. hardly anyone uses range queries on things like names,
> it's all just equality searches. Which means the bloom indexes are a
> much better match for this use case.
>
>
> The other thing we were considering was using the new multi-minmax
> opclasses as default ones, replacing the existing minmax ones. IMHO we
> shouldn't do that either. For existing minmax indexes that's useless
> (the opclass seems to be working, otherwise the index would be dropped).
> But even for new indexes I'm not sure it's the right thing, so I don't
> plan to change this.
>
>
> I'm also attaching the stress-test that I used to test the hell out of
> this, creating indexes on various data sets, data types, with varying
> index parameters, etc.
>
>
> regards
>
> --
> Tomas Vondra
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>

^ permalink  raw  reply  [nested|flat] 177+ messages in thread

* Re: WIP: BRIN multi-range indexes
  2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-25 16:51 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-10-30 20:51   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-11-18 20:45     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-19 19:38       ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2017-12-20 01:16         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2017-12-20 02:37           ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-01-07 16:13             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-01-23 20:05               ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2018-01-23 21:07                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 18:57                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 20:27                     ` Re: WIP: BRIN multi-range indexes Mark Dilger <hornschnorter@gmail.com>
  2018-02-05 20:59                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-05 23:40                         ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-02-06 00:25                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-02-25 00:30                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-02 03:55                               ` Re: WIP: BRIN multi-range indexes Andres Freund <andres@anarazel.de>
  2018-03-02 04:08                                 ` Re: WIP: BRIN multi-range indexes Tom Lane <tgl@sss.pgh.pa.us>
  2018-03-04 00:14                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-03-21 00:07                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2018-04-03 19:50                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-02 09:00                                         ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-03-02 21:25                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-03-03 04:29                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2019-06-11 17:30                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-03 22:05                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-14 17:30                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-09-25 20:07                                                     ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2019-09-26 19:01                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2019-12-01 01:55                                                         ` Re: WIP: BRIN multi-range indexes Michael Paquier <michael@paquier.xyz>
  2020-04-02 02:29                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 15:29                                                             ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 15:53                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 16:33                                                                 ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-04-05 16:59                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-04-05 17:01                                                                     ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <a.korotkov@postgrespro.ru>
  2020-07-03 00:57                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-10 09:01                                                                         ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-10 12:09                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-12 23:58                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 00:14                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-13 00:33                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-07-13 05:54                                                                                   ` Re: WIP: BRIN multi-range indexes Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
  2020-07-13 14:58                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-07-15 02:34                                                                                       ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-07-19 15:19                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-04 14:36                                                                                           ` Re: WIP: BRIN multi-range indexes Alexander Korotkov <aekorotkov@gmail.com>
  2020-08-04 15:17                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-08-07 16:27                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 16:04                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-09 18:40                                                                                                   ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 19:35                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-09 19:53                                                                                                       ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-09 20:26                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 18:32                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-10 20:01                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-11 10:14                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 14:08                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-11 18:05                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-11 19:19                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-12 16:13                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-13 16:40                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 14:33                                                                                                                           ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 16:34                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-17 21:42                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-17 22:48                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 11:40                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-18 21:06                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-18 22:27                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-21 17:42                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-21 19:56                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-24 21:18                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-24 23:50                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-28 20:42                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-09-29 02:12                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-09-30 11:57                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@2ndquadrant.com>
  2020-10-01 18:41                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-10-01 22:21                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2020-11-02 18:05                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
  2020-11-02 23:25                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
  2020-11-07 20:38                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 14:29                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-11-09 17:39                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2020-11-09 19:20                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2020-12-20 00:16                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-12 17:28                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-01-13 01:09                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-01-26 18:40                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-02-03 23:54                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-02-09 14:46                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-02-09 23:15                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-02-11 14:51                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-02-15 14:37                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-02-22 20:16                                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-03-02 23:32                                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-02 23:44                                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2021-03-02 23:53                                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-03 00:17                                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Alvaro Herrera <alvherre@alvh.no-ip.org>
  2021-03-03 01:17                                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-05 00:37                                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-08 00:53                                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-09 20:51                                                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-03-09 22:13                                                                                                                                                                                                           ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-11 16:26                                                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-17 18:59                                                                                                                                                                                                               ` Re: WIP: BRIN multi-range indexes John Naylor <john.naylor@enterprisedb.com>
  2021-03-22 05:27                                                                                                                                                                                                                 ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-22 09:10                                                                                                                                                                                                                   ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-23 01:13                                                                                                                                                                                                                     ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-26 13:08                                                                                                                                                                                                                       ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  2021-03-26 13:38                                                                                                                                                                                                                         ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
@ 2021-03-26 13:55                                                                                                                                                                                                                           ` Tom Lane <tgl@sss.pgh.pa.us>
  2021-03-26 14:04                                                                                                                                                                                                                             ` Re: WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@enterprisedb.com>
  0 siblings, 1 reply; 177+ messages in thread

From: Tom Lane @ 2021-03-26 13:55 UTC (permalink / raw)
  To: Tomas Vondra <tomas.vondra@enterprisedb.com>; +Cc: John Naylor <john.naylor@enterprisedb.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
> I recall seeing "bus error" on sparc with other patches because of
> alignment issues, so I wonder if this is what's happening here.

Try compiling with the address sanitizer enabled.  Per c.h,

 * Testing can be done with "-fsanitize=alignment -fsanitize-trap=alignment"
 * on clang, or "-fsanitize=alignment -fno-sanitize-recover=alignment" on gcc.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 177+ messages in thread

55 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.


end of thread, other threads:[~2021-03-27 20:42 UTC | newest]

Thread overview: 177+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-10-25 16:22 WIP: BRIN multi-range indexes Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-10-25 16:51 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-10-30 20:51   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-11-18 20:45     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-11-30 04:20       ` Michael Paquier <michael.paquier@gmail.com>
2017-12-19 19:38       ` Mark Dilger <hornschnorter@gmail.com>
2017-12-20 01:16         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-12-20 02:07           ` Mark Dilger <hornschnorter@gmail.com>
2017-12-20 02:37           ` Mark Dilger <hornschnorter@gmail.com>
2018-01-07 16:13             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-01-23 20:05               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-23 21:07                 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-02-05 18:57                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-02-05 20:27                     ` Mark Dilger <hornschnorter@gmail.com>
2018-02-05 20:59                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-02-05 23:40                         ` Tom Lane <tgl@sss.pgh.pa.us>
2018-02-06 00:25                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-02-25 00:30                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-03-02 03:55                               ` Andres Freund <andres@anarazel.de>
2018-03-02 04:08                                 ` Tom Lane <tgl@sss.pgh.pa.us>
2018-03-02 17:09                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-03-04 00:14                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-03-21 00:07                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-04-03 19:50                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-06-24 02:01                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-06-24 21:39                                           ` Thomas Munro <thomas.munro@enterprisedb.com>
2018-06-24 22:31                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-06-25 00:14                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2018-10-02 02:49                                                 ` Michael Paquier <michael@paquier.xyz>
2019-02-04 05:54                                                   ` Michael Paquier <michael@paquier.xyz>
2019-02-04 10:05                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-02-04 10:13                                                       ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-02-23 22:23                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-03-02 09:00                                         ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-03-02 21:25                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-03-03 04:29                                             ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-06-11 17:30                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-09-03 22:05                                                 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-09-14 17:30                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-09-25 20:07                                                     ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-09-26 19:01                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-12-01 01:55                                                         ` Michael Paquier <michael@paquier.xyz>
2020-04-02 02:29                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-04-05 15:29                                                             ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2020-04-05 15:51                                                               ` Tom Lane <tgl@sss.pgh.pa.us>
2020-04-05 16:19                                                                 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2020-04-05 15:53                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-04-05 16:33                                                                 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2020-04-05 16:59                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-04-05 17:01                                                                     ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2020-05-13 01:01                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-03 00:57                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-10 09:01                                                                         ` Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
2020-07-10 12:09                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-10 14:44                                                                             ` Sascha Kuhl <yogidabanli@gmail.com>
2020-07-11 11:24                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-11 13:32                                                                                 ` Sascha Kuhl <yogidabanli@gmail.com>
2020-07-11 13:37                                                                                   ` Sascha Kuhl <yogidabanli@gmail.com>
2020-07-11 23:30                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-12 00:06                                                                                     ` Sascha Kuhl <yogidabanli@gmail.com>
2020-07-12 23:58                                                                             ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-07-13 00:14                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-13 00:33                                                                                 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-07-13 05:54                                                                                   ` Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
2020-07-13 14:58                                                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-07-15 02:34                                                                                       ` Alexander Korotkov <aekorotkov@gmail.com>
2020-07-19 15:19                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-08-04 14:36                                                                                           ` Alexander Korotkov <aekorotkov@gmail.com>
2020-08-04 15:17                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-08-07 16:27                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-09 16:04                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-09 18:06                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-09 18:40                                                                                                   ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-09-09 19:35                                                                                                     ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-09 19:53                                                                                                       ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-09-09 20:26                                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-10 18:32                                                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-10 20:01                                                                                                             ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-09-11 10:14                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-11 14:08                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-11 18:05                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-11 19:19                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-12 16:13                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-13 16:40                                                                                                                         ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-17 14:33                                                                                                                           ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-17 16:34                                                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-17 19:19                                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-17 21:42                                                                                                                               ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-17 22:48                                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-18 11:40                                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-18 21:06                                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-18 22:27                                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-21 17:42                                                                                                                                         ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-21 19:56                                                                                                                                           ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-24 21:18                                                                                                                                             ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-24 23:50                                                                                                                                               ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-28 20:42                                                                                                                                                 ` John Naylor <john.naylor@2ndquadrant.com>
2020-09-29 02:12                                                                                                                                                   ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-30 11:57                                                                                                                                                     ` John Naylor <john.naylor@2ndquadrant.com>
2020-10-01 18:41                                                                                                                                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-10-01 22:21                                                                                                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2020-11-02 18:05                                                                                                                                                           ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2020-11-02 23:25                                                                                                                                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-11-07 20:38                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2020-11-08 18:21                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2020-11-09 14:29                                                                                                                                                                 ` John Naylor <john.naylor@enterprisedb.com>
2020-11-09 17:39                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2020-11-09 19:20                                                                                                                                                                     ` John Naylor <john.naylor@enterprisedb.com>
2020-12-20 00:16                                                                                                                                                                       ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-02 06:42                                                                                                                                                                         ` Thomas Munro <thomas.munro@gmail.com>
2021-01-02 22:56                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-12 17:28                                                                                                                                                                         ` John Naylor <john.naylor@enterprisedb.com>
2021-01-12 17:42                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-19 20:44                                                                                                                                                                             ` John Naylor <john.naylor@enterprisedb.com>
2021-01-20 00:07                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-22 01:05                                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-22 23:27                                                                                                                                                                                   ` John Naylor <john.naylor@enterprisedb.com>
2021-01-23 02:59                                                                                                                                                                                     ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-26 18:52                                                                                                                                                                                       ` John Naylor <john.naylor@enterprisedb.com>
2021-01-26 22:59                                                                                                                                                                                         ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-30 19:19                                                                                                                                                                                           ` John Naylor <john.naylor@enterprisedb.com>
2021-01-13 01:09                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-13 23:11                                                                                                                                                                             ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-14 14:32                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-01-26 18:40                                                                                                                                                                             ` John Naylor <john.naylor@enterprisedb.com>
2021-02-03 23:54                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-02-04 00:49                                                                                                                                                                                 ` Zhihong Yu <zyu@yugabyte.com>
2021-02-04 00:59                                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-02-04 01:35                                                                                                                                                                                     ` Zhihong Yu <zyu@yugabyte.com>
2021-02-09 14:46                                                                                                                                                                                 ` John Naylor <john.naylor@enterprisedb.com>
2021-02-09 23:15                                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-02-11 14:51                                                                                                                                                                                     ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-02-15 14:37                                                                                                                                                                                       ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-02-22 20:16                                                                                                                                                                                         ` John Naylor <john.naylor@enterprisedb.com>
2021-03-02 23:32                                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-02 23:44                                                                                                                                                                                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-02 23:53                                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-03 00:17                                                                                                                                                                                                 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-03 01:17                                                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-05 00:37                                                                                                                                                                                                     ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-08 00:53                                                                                                                                                                                                       ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-08 12:50                                                                                                                                                                                                         ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-09 20:51                                                                                                                                                                                                         ` John Naylor <john.naylor@enterprisedb.com>
2021-03-09 22:13                                                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-11 16:26                                                                                                                                                                                                             ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-17 18:59                                                                                                                                                                                                               ` John Naylor <john.naylor@enterprisedb.com>
2021-03-17 19:16                                                                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-17 20:55                                                                                                                                                                                                                   ` John Naylor <john.naylor@enterprisedb.com>
2021-03-22 05:27                                                                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-22 09:10                                                                                                                                                                                                                   ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-23 01:13                                                                                                                                                                                                                     ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-26 13:08                                                                                                                                                                                                                       ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-26 13:34                                                                                                                                                                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-26 13:38                                                                                                                                                                                                                         ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-26 13:55                                                                                                                                                                                                                           ` Tom Lane <tgl@sss.pgh.pa.us>
2021-03-26 14:04                                                                                                                                                                                                                             ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-26 14:45                                                                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-26 15:54                                                                                                                                                                                                                                 ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-27 18:09                                                                                                                                                                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-27 20:42                                                                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-23 13:36                                                                                                                                                                                                                     ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-23 17:56                                                                                                                                                                                                                       ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-23 18:28                                                                                                                                                                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-26 12:57                                                                                                                                                                                                                           ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2021-03-04 16:30                                                                                                                                                                                             ` John Naylor <john.naylor@enterprisedb.com>
2021-03-05 00:14                                                                                                                                                                                               ` Tomas Vondra <tomas.vondra@enterprisedb.com>
2020-09-18 11:38                                                                                                                                 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2020-09-10 20:42                                                                                                             ` John Naylor <john.naylor@2ndquadrant.com>
2019-09-10 08:19                                                 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-03-02 09:05                                     ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-03-02 21:12                                       ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-03-03 04:35                                         ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-03-12 10:33                                           ` Nikita Glukhov <n.gluhov@postgrespro.ru>
2019-03-12 17:14                                             ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-03-13 08:15                                               ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-03-13 09:51                                                 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2019-03-13 11:09                                                   ` Alexander Korotkov <a.korotkov@postgrespro.ru>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox