Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 36ea99c

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 a8f1efc commit 36ea99c

File tree

3 files changed

+109
-5
lines changed

3 files changed

+109
-5
lines changed

src/backend/utils/cache/typcache.c

+85-5
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ static void cache_array_element_properties(TypeCacheEntry *typentry);
279279
static bool record_fields_have_equality(TypeCacheEntry *typentry);
280280
static bool record_fields_have_compare(TypeCacheEntry *typentry);
281281
static void cache_record_field_properties(TypeCacheEntry *typentry);
282+
static bool range_element_has_hashing(TypeCacheEntry *typentry);
283+
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
284+
static void cache_range_element_properties(TypeCacheEntry *typentry);
282285
static void TypeCacheRelCallback(Datum arg, Oid relid);
283286
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
284287
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -480,9 +483,11 @@ lookup_type_cache(Oid type_id, int flags)
480483

481484
/*
482485
* If the proposed equality operator is array_eq or record_eq, check
483-
* to see if the element type or column types support equality. If
486+
* to see if the element type or column types support equality. If
484487
* not, array_eq or record_eq would fail at runtime, so we don't want
485-
* to report that the type has equality.
488+
* to report that the type has equality. (We can omit similar
489+
* checking for ranges because ranges can't be created in the first
490+
* place unless their subtypes support equality.)
486491
*/
487492
if (eq_opr == ARRAY_EQ_OP &&
488493
!array_element_has_equality(typentry))
@@ -517,7 +522,10 @@ lookup_type_cache(Oid type_id, int flags)
517522
typentry->btree_opintype,
518523
BTLessStrategyNumber);
519524

520-
/* As above, make sure array_cmp or record_cmp will succeed */
525+
/*
526+
* As above, make sure array_cmp or record_cmp will succeed; but again
527+
* we need no special check for ranges.
528+
*/
521529
if (lt_opr == ARRAY_LT_OP &&
522530
!array_element_has_compare(typentry))
523531
lt_opr = InvalidOid;
@@ -539,7 +547,10 @@ lookup_type_cache(Oid type_id, int flags)
539547
typentry->btree_opintype,
540548
BTGreaterStrategyNumber);
541549

542-
/* As above, make sure array_cmp or record_cmp will succeed */
550+
/*
551+
* As above, make sure array_cmp or record_cmp will succeed; but again
552+
* we need no special check for ranges.
553+
*/
543554
if (gt_opr == ARRAY_GT_OP &&
544555
!array_element_has_compare(typentry))
545556
gt_opr = InvalidOid;
@@ -561,7 +572,10 @@ lookup_type_cache(Oid type_id, int flags)
561572
typentry->btree_opintype,
562573
BTORDER_PROC);
563574

564-
/* As above, make sure array_cmp or record_cmp will succeed */
575+
/*
576+
* As above, make sure array_cmp or record_cmp will succeed; but again
577+
* we need no special check for ranges.
578+
*/
565579
if (cmp_proc == F_BTARRAYCMP &&
566580
!array_element_has_compare(typentry))
567581
cmp_proc = InvalidOid;
@@ -605,6 +619,13 @@ lookup_type_cache(Oid type_id, int flags)
605619
!array_element_has_hashing(typentry))
606620
hash_proc = InvalidOid;
607621

622+
/*
623+
* Likewise for hash_range.
624+
*/
625+
if (hash_proc == F_HASH_RANGE &&
626+
!range_element_has_hashing(typentry))
627+
hash_proc = InvalidOid;
628+
608629
/* Force update of hash_proc_finfo only if we're changing state */
609630
if (typentry->hash_proc != hash_proc)
610631
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -642,6 +663,13 @@ lookup_type_cache(Oid type_id, int flags)
642663
!array_element_has_extended_hashing(typentry))
643664
hash_extended_proc = InvalidOid;
644665

666+
/*
667+
* Likewise for hash_range_extended.
668+
*/
669+
if (hash_extended_proc == F_HASH_RANGE_EXTENDED &&
670+
!range_element_has_extended_hashing(typentry))
671+
hash_extended_proc = InvalidOid;
672+
645673
/* Force update of proc finfo only if we're changing state */
646674
if (typentry->hash_extended_proc != hash_extended_proc)
647675
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -1305,6 +1333,10 @@ cache_array_element_properties(TypeCacheEntry *typentry)
13051333
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
13061334
}
13071335

1336+
/*
1337+
* Likewise, some helper functions for composite types.
1338+
*/
1339+
13081340
static bool
13091341
record_fields_have_equality(TypeCacheEntry *typentry)
13101342
{
@@ -1376,6 +1408,54 @@ cache_record_field_properties(TypeCacheEntry *typentry)
13761408
typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
13771409
}
13781410

1411+
/*
1412+
* Likewise, some helper functions for range types.
1413+
*
1414+
* We can borrow the flag bits for array element properties to use for range
1415+
* element properties, since those flag bits otherwise have no use in a
1416+
* range type's typcache entry.
1417+
*/
1418+
1419+
static bool
1420+
range_element_has_hashing(TypeCacheEntry *typentry)
1421+
{
1422+
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1423+
cache_range_element_properties(typentry);
1424+
return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
1425+
}
1426+
1427+
static bool
1428+
range_element_has_extended_hashing(TypeCacheEntry *typentry)
1429+
{
1430+
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
1431+
cache_range_element_properties(typentry);
1432+
return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
1433+
}
1434+
1435+
static void
1436+
cache_range_element_properties(TypeCacheEntry *typentry)
1437+
{
1438+
/* load up subtype link if we didn't already */
1439+
if (typentry->rngelemtype == NULL &&
1440+
typentry->typtype == TYPTYPE_RANGE)
1441+
load_rangetype_info(typentry);
1442+
1443+
if (typentry->rngelemtype != NULL)
1444+
{
1445+
TypeCacheEntry *elementry;
1446+
1447+
/* might need to calculate subtype's hash function properties */
1448+
elementry = lookup_type_cache(typentry->rngelemtype->type_id,
1449+
TYPECACHE_HASH_PROC |
1450+
TYPECACHE_HASH_EXTENDED_PROC);
1451+
if (OidIsValid(elementry->hash_proc))
1452+
typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
1453+
if (OidIsValid(elementry->hash_extended_proc))
1454+
typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
1455+
}
1456+
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
1457+
}
1458+
13791459
/*
13801460
* Make sure that RecordCacheArray is large enough to store 'typmod'.
13811461
*/

src/test/regress/expected/rangetypes.out

+12
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,18 @@ select *, row_to_json(upper(t)) as u from
13541354
drop type two_ints cascade;
13551355
NOTICE: drop cascades to type two_ints_range
13561356
--
1357+
-- Check behavior when subtype lacks a hash function
1358+
--
1359+
create type cashrange as range (subtype = money);
1360+
set enable_sort = off; -- try to make it pick a hash setop implementation
1361+
select '(2,5)'::cashrange except select '(5,6)'::cashrange;
1362+
cashrange
1363+
---------------
1364+
($2.00,$5.00)
1365+
(1 row)
1366+
1367+
reset enable_sort;
1368+
--
13571369
-- OUT/INOUT/TABLE functions
13581370
--
13591371
create function outparam_succeed(i anyrange, out r anyrange, out t text)

src/test/regress/sql/rangetypes.sql

+12
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,18 @@ select *, row_to_json(upper(t)) as u from
461461

462462
drop type two_ints cascade;
463463

464+
--
465+
-- Check behavior when subtype lacks a hash function
466+
--
467+
468+
create type cashrange as range (subtype = money);
469+
470+
set enable_sort = off; -- try to make it pick a hash setop implementation
471+
472+
select '(2,5)'::cashrange except select '(5,6)'::cashrange;
473+
474+
reset enable_sort;
475+
464476
--
465477
-- OUT/INOUT/TABLE functions
466478
--

0 commit comments

Comments
 (0)