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
>From c27f02d2839e08ebcee852448ed3838c8932d2ea Mon Sep 17 00:00:00 2001 From: Tomas Vondra <to...@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
>From 723d186a99a8eb6f5595713615d4ad85bc4167bc Mon Sep 17 00:00:00 2001 From: Tomas Vondra <to...@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
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers