]> granicus.if.org Git - postgresql/commitdiff
Fix typcache's failure to treat ranges as container types.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 20 Oct 2017 21:12:28 +0000 (17:12 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 20 Oct 2017 21:12:28 +0000 (17:12 -0400)
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.

src/backend/utils/cache/typcache.c
src/test/regress/expected/rangetypes.out
src/test/regress/sql/rangetypes.sql

index 645e333a398ae780ca40849abd8feabb318bfe19..6285bab08ff7d1a7746a8e7a97c7e7db45186469 100644 (file)
@@ -129,6 +129,8 @@ static void cache_array_element_properties(TypeCacheEntry *typentry);
 static bool record_fields_have_equality(TypeCacheEntry *typentry);
 static bool record_fields_have_compare(TypeCacheEntry *typentry);
 static void cache_record_field_properties(TypeCacheEntry *typentry);
+static bool range_element_has_hashing(TypeCacheEntry *typentry);
+static void cache_range_element_properties(TypeCacheEntry *typentry);
 static void TypeCacheRelCallback(Datum arg, Oid relid);
 static void load_enum_cache_data(TypeCacheEntry *tcache);
 static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
@@ -405,6 +407,13 @@ lookup_type_cache(Oid type_id, int flags)
                        !array_element_has_hashing(typentry))
                        hash_proc = InvalidOid;
 
+               /*
+                * Likewise for hash_range.
+                */
+               if (hash_proc == F_HASH_RANGE &&
+                       !range_element_has_hashing(typentry))
+                       hash_proc = InvalidOid;
+
                typentry->hash_proc = hash_proc;
        }
 
@@ -608,6 +617,10 @@ cache_array_element_properties(TypeCacheEntry *typentry)
        typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
 }
 
+/*
+ * Likewise, some helper functions for composite types.
+ */
+
 static bool
 record_fields_have_equality(TypeCacheEntry *typentry)
 {
@@ -678,6 +691,43 @@ cache_record_field_properties(TypeCacheEntry *typentry)
        typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
 }
 
+/*
+ * Likewise, some helper functions for range 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.
+ */
+
+static bool
+range_element_has_hashing(TypeCacheEntry *typentry)
+{
+       if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+               cache_range_element_properties(typentry);
+       return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static void
+cache_range_element_properties(TypeCacheEntry *typentry)
+{
+       /* load up subtype link if we didn't already */
+       if (typentry->rngelemtype == NULL &&
+               typentry->typtype == TYPTYPE_RANGE)
+               load_rangetype_info(typentry);
+
+       if (typentry->rngelemtype != NULL)
+       {
+               TypeCacheEntry *elementry;
+
+               /* might need to calculate subtype's hash function properties */
+               elementry = lookup_type_cache(typentry->rngelemtype->type_id,
+                                                                         TYPECACHE_HASH_PROC);
+               if (OidIsValid(elementry->hash_proc))
+                       typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+       }
+       typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
 
 /*
  * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
index 094b89493ea1202ba3446fa9cf3a8d9a9107ffd6..e668244c2769a4eb0643fe79cc1cd6002c342406 100644 (file)
@@ -1039,6 +1039,18 @@ select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
  t
 (1 row)
 
+--
+-- Check behavior when subtype lacks a hash function
+--
+create type cashrange as range (subtype = money);
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+select '(2,5)'::cashrange except select '(5,6)'::cashrange;
+   cashrange   
+---------------
+ ($2.00,$5.00)
+(1 row)
+
+reset enable_sort;
 --
 -- OUT/INOUT/TABLE functions
 --
index 08d6976e045621955d86c24ad9a132fcb489af5c..faf4664e436d7efed9bec88f749b07ae2cb5bac3 100644 (file)
@@ -363,6 +363,18 @@ select arrayrange(ARRAY[2,1], ARRAY[1,2]);  -- fail
 select array[1,1] <@ arrayrange(array[1,2], array[2,1]);
 select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
 
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+create type cashrange as range (subtype = money);
+
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+
+select '(2,5)'::cashrange except select '(5,6)'::cashrange;
+
+reset enable_sort;
+
 --
 -- OUT/INOUT/TABLE functions
 --