Hi Jesper,

On 2019/04/09 1:33, Jesper Pedersen wrote:
> Hi,
> 
> The following case
> 
> -- test.sql --
> CREATE TABLE test (a text PRIMARY KEY, b text) PARTITION BY HASH (a);
> CREATE TABLE test_p0 PARTITION OF test FOR VALUES WITH (MODULUS 2,
> REMAINDER 0);
> CREATE TABLE test_p1 PARTITION OF test FOR VALUES WITH (MODULUS 2,
> REMAINDER 1);
> -- CREATE INDEX idx_test_b ON test USING HASH (b);
> 
> INSERT INTO test VALUES ('aaaa', 'aaaa');
> 
> -- Regression
> UPDATE test SET b = 'bbbb' WHERE a = 'aaaa';
> -- test.sql --
> 
> fails on master, which includes [1], with
> 
> 
> psql:test.sql:9: ERROR:  could not determine which collation to use for
> string hashing
> HINT:  Use the COLLATE clause to set the collation explicitly.
> 
> 
> It passes on 11.x.

Thanks for the report.

This seems to broken since the following commit (I see you already cc'd
Peter):

commit 5e1963fb764e9cc092e0f7b58b28985c311431d9
Author: Peter Eisentraut <pe...@eisentraut.org>
Date:   Fri Mar 22 12:09:32 2019 +0100

    Collations with nondeterministic comparison


As of this commit, hashing functions hashtext() and hashtextextended()
require a valid collation to be passed in.  ISTM,
satisfies_hash_partition() that's called by hash partition constraint
checking should have been changed to use FunctionCall2Coll() interface to
account for the requirements of the above commit.  I see that it did that
for compute_partition_hash_value(), which is used by hash partition tuple
routing.  That also seems to be covered by regression tests, but there are
no tests that cover satisfies_hash_partition().

Attached patch is an attempt to fix this.  I've also added Amul Sul who
can maybe comment on the satisfies_hash_partition() changes.

BTW, it seems we don't need to back-patch this to PG 11 which introduced
hash partitioning, because text hashing functions don't need collation
there, right?

Thanks,
Amit
diff --git a/src/backend/partitioning/partbounds.c 
b/src/backend/partitioning/partbounds.c
index c8770ccfee..331dab3954 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -2778,6 +2778,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
                bool            variadic_typbyval;
                char            variadic_typalign;
                FmgrInfo        partsupfunc[PARTITION_MAX_KEYS];
+               Oid                     partcollid[PARTITION_MAX_KEYS];
        } ColumnsHashData;
        Oid                     parentId;
        int                     modulus;
@@ -2865,6 +2866,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
                                fmgr_info_copy(&my_extra->partsupfunc[j],
                                                           &key->partsupfunc[j],
                                                           
fcinfo->flinfo->fn_mcxt);
+                               my_extra->partcollid[j] = key->partcollation[j];
                        }
 
                }
@@ -2888,6 +2890,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 
                        /* check argument types */
                        for (j = 0; j < key->partnatts; ++j)
+                       {
                                if (key->parttypid[j] != 
my_extra->variadic_type)
                                        ereport(ERROR,
                                                        
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2895,6 +2898,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
                                                                        j + 1,
                                                                        
format_type_be(key->parttypid[j]),
                                                                        
format_type_be(my_extra->variadic_type))));
+                               my_extra->partcollid[j] = key->partcollation[j];
+                       }
 
                        fmgr_info_copy(&my_extra->partsupfunc[0],
                                                   &key->partsupfunc[0],
@@ -2928,9 +2933,10 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 
                        Assert(OidIsValid(my_extra->partsupfunc[i].fn_oid));
 
-                       hash = FunctionCall2(&my_extra->partsupfunc[i],
-                                                                
PG_GETARG_DATUM(argno),
-                                                                seed);
+                       hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
+                                                                        
my_extra->partcollid[i],
+                                                                        
PG_GETARG_DATUM(argno),
+                                                                        seed);
 
                        /* Form a single 64-bit hash value */
                        rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
diff --git a/src/test/regress/expected/hash_part.out 
b/src/test/regress/expected/hash_part.out
index 731d26fc3d..b536b6955c 100644
--- a/src/test/regress/expected/hash_part.out
+++ b/src/test/regress/expected/hash_part.out
@@ -99,6 +99,20 @@ ERROR:  number of partitioning columns (2) does not match 
number of partition ke
 SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
                                                                variadic 
array[now(), now()]);
 ERROR:  column 1 of the partition key has type "integer", but supplied value 
is of type "timestamp with time zone"
+-- check satisfies_hash_partition passes correct collation
+create table text_hashp (a text) partition by hash (a);
+create table text_hashp0 partition of text_hashp for values with (modulus 2, 
remainder 0);
+create table text_hashp1 partition of text_hashp for values with (modulus 2, 
remainder 1);
+-- The result here should always be true, because 'xxx' must belong at least
+-- one of the partitions
+select satisfies_hash_partition('text_hashp'::regclass, 2, 0, 'xxx'::text) OR
+          satisfies_hash_partition('text_hashp'::regclass, 2, 1, 'xxx'::text) 
AS satisfies;
+ satisfies 
+-----------
+ t
+(1 row)
+
 -- cleanup
 DROP TABLE mchash;
 DROP TABLE mcinthash;
+DROP TABLE text_hashp;
diff --git a/src/test/regress/sql/hash_part.sql 
b/src/test/regress/sql/hash_part.sql
index f457ac344c..30601b913e 100644
--- a/src/test/regress/sql/hash_part.sql
+++ b/src/test/regress/sql/hash_part.sql
@@ -75,6 +75,16 @@ SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
 SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
                                                                variadic 
array[now(), now()]);
 
+-- check satisfies_hash_partition passes correct collation
+create table text_hashp (a text) partition by hash (a);
+create table text_hashp0 partition of text_hashp for values with (modulus 2, 
remainder 0);
+create table text_hashp1 partition of text_hashp for values with (modulus 2, 
remainder 1);
+-- The result here should always be true, because 'xxx' must belong to
+-- one of the two defined partitions
+select satisfies_hash_partition('text_hashp'::regclass, 2, 0, 'xxx'::text) OR
+          satisfies_hash_partition('text_hashp'::regclass, 2, 1, 'xxx'::text) 
AS satisfies;
+
 -- cleanup
 DROP TABLE mchash;
 DROP TABLE mcinthash;
+DROP TABLE text_hashp;

Reply via email to