diff options
author | Alexander Korotkov <akorotkov@postgresql.org> | 2020-12-20 07:20:33 +0300 |
---|---|---|
committer | Alexander Korotkov <akorotkov@postgresql.org> | 2020-12-20 07:20:33 +0300 |
commit | 6df7a9698bb036610c1e8c6d375e1be38cb26d5f (patch) | |
tree | dc5985310422e2b41e06bc1770f9e384e0bd6525 /src/backend/utils/cache/typcache.c | |
parent | 08b01d4dd982b491a2f9641804b368185b8f4c53 (diff) | |
download | postgresql-6df7a9698bb036610c1e8c6d375e1be38cb26d5f.tar.gz postgresql-6df7a9698bb036610c1e8c6d375e1be38cb26d5f.zip |
Multirange datatypes
Multiranges are basically sorted arrays of non-overlapping ranges with
set-theoretic operations defined over them.
Since v14, each range type automatically gets a corresponding multirange
datatype. There are both manual and automatic mechanisms for naming multirange
types. Once can specify multirange type name using multirange_type_name
attribute in CREATE TYPE. Otherwise, a multirange type name is generated
automatically. If the range type name contains "range" then we change that to
"multirange". Otherwise, we add "_multirange" to the end.
Implementation of multiranges comes with a space-efficient internal
representation format, which evades extra paddings and duplicated storage of
oids. Altogether this format allows fetching a particular range by its index
in O(n).
Statistic gathering and selectivity estimation are implemented for multiranges.
For this purpose, stored multirange is approximated as union range without gaps.
This field will likely need improvements in the future.
Catversion is bumped.
Discussion: https://postgr.es/m/CALNJ-vSUpQ_Y%3DjXvTxt1VYFztaBSsWVXeF1y6gTYQ4bOiWDLgQ%40mail.gmail.com
Discussion: https://postgr.es/m/a0b8026459d1e6167933be2104a6174e7d40d0ab.camel%40j-davis.com#fe7218c83b08068bfffb0c5293eceda0
Author: Paul Jungwirth, revised by me
Reviewed-by: David Fetter, Corey Huinker, Jeff Davis, Pavel Stehule
Reviewed-by: Alvaro Herrera, Tom Lane, Isaac Morland, David G. Johnston
Reviewed-by: Zhihong Yu, Alexander Korotkov
Diffstat (limited to 'src/backend/utils/cache/typcache.c')
-rw-r--r-- | src/backend/utils/cache/typcache.c | 98 |
1 files changed, 91 insertions, 7 deletions
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 1e331098c0d..8c97ef39557 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -287,6 +287,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER; static void load_typcache_tupdesc(TypeCacheEntry *typentry); static void load_rangetype_info(TypeCacheEntry *typentry); +static void load_multirangetype_info(TypeCacheEntry *typentry); static void load_domaintype_info(TypeCacheEntry *typentry); static int dcs_cmp(const void *a, const void *b); static void decr_dcc_refcount(DomainConstraintCache *dcc); @@ -305,6 +306,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry); static bool range_element_has_hashing(TypeCacheEntry *typentry); static bool range_element_has_extended_hashing(TypeCacheEntry *typentry); static void cache_range_element_properties(TypeCacheEntry *typentry); +static bool multirange_element_has_hashing(TypeCacheEntry *typentry); +static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry); +static void cache_multirange_element_properties(TypeCacheEntry *typentry); static void TypeCacheRelCallback(Datum arg, Oid relid); static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue); static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue); @@ -557,8 +561,8 @@ lookup_type_cache(Oid type_id, int flags) * to see if the element type or column types support equality. If * not, array_eq or record_eq would fail at runtime, so we don't want * to report that the type has equality. (We can omit similar - * checking for ranges because ranges can't be created in the first - * place unless their subtypes support equality.) + * checking for ranges and multiranges because ranges can't be created + * in the first place unless their subtypes support equality.) */ if (eq_opr == ARRAY_EQ_OP && !array_element_has_equality(typentry)) @@ -595,7 +599,7 @@ lookup_type_cache(Oid type_id, int flags) /* * As above, make sure array_cmp or record_cmp will succeed; but again - * we need no special check for ranges. + * we need no special check for ranges or multiranges. */ if (lt_opr == ARRAY_LT_OP && !array_element_has_compare(typentry)) @@ -620,7 +624,7 @@ lookup_type_cache(Oid type_id, int flags) /* * As above, make sure array_cmp or record_cmp will succeed; but again - * we need no special check for ranges. + * we need no special check for ranges or multiranges. */ if (gt_opr == ARRAY_GT_OP && !array_element_has_compare(typentry)) @@ -645,7 +649,7 @@ lookup_type_cache(Oid type_id, int flags) /* * As above, make sure array_cmp or record_cmp will succeed; but again - * we need no special check for ranges. + * we need no special check for ranges or multiranges. */ if (cmp_proc == F_BTARRAYCMP && !array_element_has_compare(typentry)) @@ -695,6 +699,13 @@ lookup_type_cache(Oid type_id, int flags) !range_element_has_hashing(typentry)) hash_proc = InvalidOid; + /* + * Likewise for hash_multirange. + */ + if (hash_proc == F_HASH_MULTIRANGE && + !multirange_element_has_hashing(typentry)) + hash_proc = InvalidOid; + /* Force update of hash_proc_finfo only if we're changing state */ if (typentry->hash_proc != hash_proc) typentry->hash_proc_finfo.fn_oid = InvalidOid; @@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags) !range_element_has_extended_hashing(typentry)) hash_extended_proc = InvalidOid; + /* + * Likewise for hash_multirange_extended. + */ + if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED && + !multirange_element_has_extended_hashing(typentry)) + hash_extended_proc = InvalidOid; + /* Force update of proc finfo only if we're changing state */ if (typentry->hash_extended_proc != hash_extended_proc) typentry->hash_extended_proc_finfo.fn_oid = InvalidOid; @@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags) } /* + * If requested, get information about a multirange type + */ + if ((flags & TYPECACHE_MULTIRANGE_INFO) && + typentry->rngtype == NULL && + typentry->typtype == TYPTYPE_MULTIRANGE) + { + load_multirangetype_info(typentry); + } + + /* * If requested, get information about a domain type */ if ((flags & TYPECACHE_DOMAIN_BASE_INFO) && @@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry) typentry->rngelemtype = lookup_type_cache(subtypeOid, 0); } +/* + * load_multirangetype_info --- helper routine to set up multirange type + * information + */ +static void +load_multirangetype_info(TypeCacheEntry *typentry) +{ + Oid rangetypeOid; + + rangetypeOid = get_multirange_range(typentry->type_id); + if (!OidIsValid(rangetypeOid)) + elog(ERROR, "cache lookup failed for multirange type %u", + typentry->type_id); + + typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO); +} /* * load_domaintype_info --- helper routine to set up domain constraint info @@ -1559,11 +1603,11 @@ cache_record_field_properties(TypeCacheEntry *typentry) } /* - * Likewise, some helper functions for range types. + * Likewise, some helper functions for range and multirange types. * * We can borrow the flag bits for array element properties to use for range * element properties, since those flag bits otherwise have no use in a - * range type's typcache entry. + * range or multirange type's typcache entry. */ static bool @@ -1606,6 +1650,46 @@ cache_range_element_properties(TypeCacheEntry *typentry) typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES; } +static bool +multirange_element_has_hashing(TypeCacheEntry *typentry) +{ + if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES)) + cache_multirange_element_properties(typentry); + return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0; +} + +static bool +multirange_element_has_extended_hashing(TypeCacheEntry *typentry) +{ + if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES)) + cache_multirange_element_properties(typentry); + return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0; +} + +static void +cache_multirange_element_properties(TypeCacheEntry *typentry) +{ + /* load up range link if we didn't already */ + if (typentry->rngtype == NULL && + typentry->typtype == TYPTYPE_MULTIRANGE) + load_multirangetype_info(typentry); + + if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL) + { + TypeCacheEntry *elementry; + + /* might need to calculate subtype's hash function properties */ + elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id, + TYPECACHE_HASH_PROC | + TYPECACHE_HASH_EXTENDED_PROC); + if (OidIsValid(elementry->hash_proc)) + typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING; + if (OidIsValid(elementry->hash_extended_proc)) + typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING; + } + typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES; +} + /* * Make sure that RecordCacheArray and RecordIdentifierArray are large enough * to store 'typmod'. |