Skip to content

Commit 63fbc51

Browse files
committed
Fix typcache's failure to treat ranges as container types.
Like the similar logic for arrays and records, it's necessary to examine the range's subtype to decide whether the range type can support hashing. We can omit checking the subtype for btree-defined operations, though, since range subtypes are required to have those operations. (Possibly that simplification for btree cases led us to overlook that it does not apply for hash cases.) This is only an issue if the subtype lacks hash support, which is not true of any built-in range type, but it's easy to demonstrate a problem with a range type over, eg, money: you can get a "could not identify a hash function" failure when the planner is misled into thinking that hash join or aggregation would work. This was born broken, so back-patch to all supported branches.
1 parent 9e20276 commit 63fbc51

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-0
lines changed

src/backend/utils/cache/typcache.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ static void cache_array_element_properties(TypeCacheEntry *typentry);
167167
static bool record_fields_have_equality(TypeCacheEntry *typentry);
168168
static bool record_fields_have_compare(TypeCacheEntry *typentry);
169169
static void cache_record_field_properties(TypeCacheEntry *typentry);
170+
static bool range_element_has_hashing(TypeCacheEntry *typentry);
171+
static void cache_range_element_properties(TypeCacheEntry *typentry);
170172
static void TypeCacheRelCallback(Datum arg, Oid relid);
171173
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
172174
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -480,6 +482,13 @@ lookup_type_cache(Oid type_id, int flags)
480482
!array_element_has_hashing(typentry))
481483
hash_proc = InvalidOid;
482484

485+
/*
486+
* Likewise for hash_range.
487+
*/
488+
if (hash_proc == F_HASH_RANGE &&
489+
!range_element_has_hashing(typentry))
490+
hash_proc = InvalidOid;
491+
483492
/* Force update of hash_proc_finfo only if we're changing state */
484493
if (typentry->hash_proc != hash_proc)
485494
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -1116,6 +1125,10 @@ cache_array_element_properties(TypeCacheEntry *typentry)
11161125
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
11171126
}
11181127

1128+
/*
1129+
* Likewise, some helper functions for composite types.
1130+
*/
1131+
11191132
static bool
11201133
record_fields_have_equality(TypeCacheEntry *typentry)
11211134
{
@@ -1186,6 +1199,43 @@ cache_record_field_properties(TypeCacheEntry *typentry)
11861199
typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
11871200
}
11881201

1202+
/*
1203+
* Likewise, some helper functions for range types.
1204+
*
1205+
* We can borrow the flag bits for array element properties to use for range
1206+
* element properties, since those flag bits otherwise have no use in a
1207+
* range type's typcache entry.
1208+
*/
1209+
1210+
static bool
1211+
range_element_has_hashing(TypeCacheEntry *typentry)
1212+
{
1213+
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1214+
cache_range_element_properties(typentry);
1215+
return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1216+
}
1217+
1218+
static void
1219+
cache_range_element_properties(TypeCacheEntry *typentry)
1220+
{
1221+
/* load up subtype link if we didn't already */
1222+
if (typentry->rngelemtype == NULL &&
1223+
typentry->typtype == TYPTYPE_RANGE)
1224+
load_rangetype_info(typentry);
1225+
1226+
if (typentry->rngelemtype != NULL)
1227+
{
1228+
TypeCacheEntry *elementry;
1229+
1230+
/* might need to calculate subtype's hash function properties */
1231+
elementry = lookup_type_cache(typentry->rngelemtype->type_id,
1232+
TYPECACHE_HASH_PROC);
1233+
if (OidIsValid(elementry->hash_proc))
1234+
typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1235+
}
1236+
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
1237+
}
1238+
11891239

11901240
/*
11911241
* lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype

src/test/regress/expected/rangetypes.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,18 @@ select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
13361336
t
13371337
(1 row)
13381338

1339+
--
1340+
-- Check behavior when subtype lacks a hash function
1341+
--
1342+
create type cashrange as range (subtype = money);
1343+
set enable_sort = off; -- try to make it pick a hash setop implementation
1344+
select '(2,5)'::cashrange except select '(5,6)'::cashrange;
1345+
cashrange
1346+
---------------
1347+
($2.00,$5.00)
1348+
(1 row)
1349+
1350+
reset enable_sort;
13391351
--
13401352
-- OUT/INOUT/TABLE functions
13411353
--

src/test/regress/sql/rangetypes.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,18 @@ select arrayrange(ARRAY[2,1], ARRAY[1,2]); -- fail
447447
select array[1,1] <@ arrayrange(array[1,2], array[2,1]);
448448
select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
449449

450+
--
451+
-- Check behavior when subtype lacks a hash function
452+
--
453+
454+
create type cashrange as range (subtype = money);
455+
456+
set enable_sort = off; -- try to make it pick a hash setop implementation
457+
458+
select '(2,5)'::cashrange except select '(5,6)'::cashrange;
459+
460+
reset enable_sort;
461+
450462
--
451463
-- OUT/INOUT/TABLE functions
452464
--

0 commit comments

Comments
 (0)