Postgresql 为Postgres分区创建自定义哈希运算符

Postgresql 为Postgres分区创建自定义哈希运算符,postgresql,hash,operator-overloading,partitioning,postgresql-13,Postgresql,Hash,Operator Overloading,Partitioning,Postgresql 13,我想创建一个自定义哈希函数,Postgres(13.2版)将使用该函数跨分区分发行。问题是,对于当前的解决方案,Postgres不使用分区修剪。 这是我的密码: -- dummy hash function CREATE OR REPLACE FUNCTION partition_custom_bigint_hash(value BIGINT, seed BIGINT) RETURNS BIGINT AS $$ SELECT value; $$ LANGUAGE SQL IMMUTABL

我想创建一个自定义哈希函数,Postgres(13.2版)将使用该函数跨分区分发行。问题是,对于当前的解决方案,Postgres不使用分区修剪。 这是我的密码:

-- dummy hash function
CREATE OR REPLACE FUNCTION partition_custom_bigint_hash(value BIGINT, seed
BIGINT)
RETURNS BIGINT AS $$
    SELECT value;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

-- operator
CREATE OPERATOR CLASS partition_custom_bigint_hash_op
    FOR TYPE int8
    USING hash AS
    OPERATOR 1 =,
    FUNCTION 2 partition_custom_bigint_hash(BIGINT, BIGINT);

-- table partitioned by hash with custom operator
CREATE TABLE sample(part_id BIGINT) PARTITION BY hash(part_id partition_custom_bigint_hash_op);
CREATE TABLE sample_part_1 PARTITION OF SAMPLE FOR VALUES WITH (modulus 3, remainder 0);
CREATE TABLE sample_part_2 PARTITION OF SAMPLE FOR VALUES WITH (modulus 3, remainder 1);
CREATE TABLE sample_part_3 PARTITION OF SAMPLE FOR VALUES WITH (modulus 3, remainder 2);
现在,请确保已启用分区修剪并正常工作:

SHOW enable_partition_pruning;
--  enable_partition_pruning
-- --------------------------
--  on

EXPLAIN * FROM sample WHERE part_id = 1::BIGINT;
--                               QUERY PLAN                              
-- ----------------------------------------------------------------------
--  Seq Scan on sample_part_1 sample  (cost=0.00..38.25 rows=11 width=8)
--    Filter: (part_id = '1'::bigint)
-- (2 rows)
因此,当使用condition
part_id=1::BIGINT
时,它可以正常工作,但如果我跳过对BIGINT的转换,我会得到:

EXPLAIN SELECT * FROM sample WHERE part_id = 1;
--                                   QUERY PLAN
-- ------------------------------------------------------------------------------
--  Append  (cost=0.00..101.36 rows=33 width=8)
--    ->  Seq Scan on sample_part_1 sample_1  (cost=0.00..33.73 rows=11 width=8)
--          Filter: (part_id = 1)
--    ->  Seq Scan on sample_part_2 sample_2  (cost=0.00..33.73 rows=11 width=8)
--          Filter: (part_id = 1)
--    ->  Seq Scan on sample_part_3 sample_3  (cost=0.00..33.73 rows=11 width=8)
--          Filter: (part_id = 1)

问题:为了使分区修剪在两种情况下都能工作,我需要更改什么?part_id=1和
part_id=1::BIGINT

有几个等式运算符,左侧有
BIGINT

SELECT oid,
       oprcode::regproc AS function,
       oprright::regtype AS right_side
FROM pg_operator
WHERE oprname = '='
  AND oprleft = 'bigint'::regtype;

 oid  | function | right_side 
------+----------+------------
  410 | int8eq   | bigint
  416 | int84eq  | integer
 1868 | int82eq  | smallint
(3 rows)
现在,第二个查询使用第二个运算符,但该运算符不属于您的自定义运算符族,因此不会进行分区修剪

请参见
src/backend/partitioning/partprune.c
中的
match_子句_to_partition_key
中的此注释:

/*
*查看运算符是否与该族相关。
*
*通常,我们只关心被列为一部分的运营商
*分区运算符族的。但有一个例外:
*不等于运算符不列在任何运算符族中
*无论如何,但是他们的否定词(相等)是。我们可以用其中一个
*如果我们找到它,就可以使用它,但只适用于列表分区。
*
*注意:我们在故障时报告NOMATCH,以防稍后的partkey具有
*相同的表达,但不同的家族。这不太可能,但不是
*比使用不同排序规则的重复表达式更重要。
*/
创建包含所需运算符的运算符族:

CREATE FUNCTION partition_custom_hash(value int8, seed int8) RETURNS int8
   AS 'SELECT value' LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION partition_custom_hash(value int4, seed int4) RETURNS int8
   AS 'SELECT value::int8' LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION partition_custom_hash(value int2, seed int2) RETURNS int8 
   AS 'SELECT value::int8' LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

CREATE OPERATOR FAMILY partition_custom_integer_hash_ops USING hash;

CREATE OPERATOR CLASS partition_custom_int8_hash_ops FOR TYPE int8 USING hash
   FAMILY partition_custom_integer_hash_ops AS
   OPERATOR 1 = (int8, int8),
   FUNCTION 2 partition_custom_hash(int8, int8);

CREATE OPERATOR CLASS partition_custom_int4_hash_ops FOR TYPE int4 USING hash
   FAMILY partition_custom_integer_hash_ops AS
   OPERATOR 1 = (int8, int4),
   FUNCTION 2 partition_custom_hash(int4, int4);

CREATE OPERATOR CLASS partition_custom_int2_hash_ops FOR TYPE int2 USING hash 
   FAMILY partition_custom_integer_hash_ops AS 
   OPERATOR 1 = (int8, int2),
   FUNCTION 2 partition_custom_hash(int2, int2);

然后,如果您使用
分区\u自定义\u int8\u散列\u ops
,它应该可以正常工作。

非常感谢!这很好用!