postgresql xxhash64 哈希函数实现

由于业务需求,只支持 int8 -> int8,如果需要改类型,可以让 AI 改下

sql 复制代码
CREATE OR REPLACE FUNCTION public.xxhash64(p_input bigint)
RETURNS bigint
LANGUAGE plpgsql
IMMUTABLE STRICT
AS $xxh$
DECLARE
    -- xxHash64 的 5 个 64 位素数(超出 int8 有符号范围,用 numeric 无损保存)
    c_p1 numeric := 11400714785074694791;
    c_p2 numeric := 14029467366897019727;
    c_p3 numeric := 1609587929392839161;
    c_p4 numeric := 9650029242287828579;
    c_p5 numeric := 2870177450012600261;
    c_mod numeric := 18446744073709551616;   -- 2^64,无符号回绕模数
    c_half numeric := 9223372036854775808;   -- 2^63,无符号<->有符号转换分界
    c_len numeric := 8;                      -- 输入字节数(int8 固定 8 字节)
    c_byte_mask bigint := 255;               -- 提取单字节用掩码(& 要求整型)
    c_shift_33 numeric := 8589934592;        -- 2^33,avalanche 逻辑右移量
    c_shift_29 numeric := 536870912;         -- 2^29
    c_shift_32 numeric := 4294967296;        -- 2^32
    v_le numeric;      -- 无符号 64 位输入值(小端展开)
    v_h numeric;       -- 无符号 64 位哈希累加器
    v_acc numeric;     -- round 中间累积值
    v_bits bit(64);    -- 供 按位(XOR/循环左移) 运算中转
    v_byte numeric;
    v_shift integer;
BEGIN
    -- 将 int8 输入按小端展开为无符号 64 位数(符号位视为 2^63)
    v_le := 0;
    FOR v_shift IN 0..7 LOOP
        -- power 必须显式 numeric,否则 PG 会解析为 double 导致 2^56 以上丢失精度
        v_byte := (p_input >> (8 * v_shift)) & c_byte_mask;
        v_le := v_le + v_byte * power(256::numeric, v_shift);
    END LOOP;

    -- xxHash64(seed=0),输入固定 8 字节,走 len<32 短路径
    v_h := mod(c_p5, c_mod);
    v_h := mod(v_h + c_len, c_mod);

    -- finalize 中处理第一个 8 字节组:k1 = XXH64_round(0, le64)
    v_acc := mod(v_le * c_p2, c_mod);
    -- rotl64(v_acc, 31):无符号值转 bit(64) 后做循环左移再转回无符号
    v_bits := (CASE WHEN v_acc >= c_half THEN v_acc - c_mod ELSE v_acc END)::bigint::bit(64);
    v_bits := (v_bits << 31) | (v_bits >> 33);
    v_acc := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                  ELSE (v_bits::bigint)::numeric END;
    v_acc := mod(v_acc * c_p1, c_mod);
    -- h ^= k1
    v_bits := (CASE WHEN v_h >= c_half THEN v_h - c_mod ELSE v_h END)::bigint::bit(64)
              # (CASE WHEN v_acc >= c_half THEN v_acc - c_mod ELSE v_acc END)::bigint::bit(64);
    v_h := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                ELSE (v_bits::bigint)::numeric END;
    -- rotl64(v_h, 27),再 h = h*P1 + P4
    v_bits := (CASE WHEN v_h >= c_half THEN v_h - c_mod ELSE v_h END)::bigint::bit(64);
    v_bits := (v_bits << 27) | (v_bits >> 37);
    v_h := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                ELSE (v_bits::bigint)::numeric END;
    v_h := mod(mod(v_h * c_p1, c_mod) + c_p4, c_mod);

    -- 8 字节已耗尽,无 4 字节/单字节尾部

    -- avalanche 归约:逻辑右移用 floor(v/2^n) 在无符号 numeric 域等价
    -- h ^= h >> 33
    v_bits := (CASE WHEN v_h >= c_half THEN v_h - c_mod ELSE v_h END)::bigint::bit(64)
              # (floor(v_h / c_shift_33))::bigint::bit(64);
    v_h := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                ELSE (v_bits::bigint)::numeric END;
    v_h := mod(v_h * c_p2, c_mod);
    -- h ^= h >> 29
    v_bits := (CASE WHEN v_h >= c_half THEN v_h - c_mod ELSE v_h END)::bigint::bit(64)
              # (floor(v_h / c_shift_29))::bigint::bit(64);
    v_h := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                ELSE (v_bits::bigint)::numeric END;
    v_h := mod(v_h * c_p3, c_mod);
    -- h ^= h >> 32
    v_bits := (CASE WHEN v_h >= c_half THEN v_h - c_mod ELSE v_h END)::bigint::bit(64)
              # (floor(v_h / c_shift_32))::bigint::bit(64);
    v_h := CASE WHEN (v_bits::bigint)::numeric < 0 THEN (v_bits::bigint)::numeric + c_mod
                ELSE (v_bits::bigint)::numeric END;

    -- 无符号值转有符号 int8 返回
    IF v_h >= c_half THEN
        v_h := v_h - c_mod;
    END IF;
    RETURN v_h::bigint;
END;
$xxh$;
相关推荐
希望奇迹很安静39 分钟前
等级保护测评
网络·数据库
会博通·代码搬运工1 小时前
疾控档案落地难点:异构业务系统并存,高价值纸质凭证如何回流业务闭环
数据库·档案管理·档案管理系统·智能扫描设备·会博通
数智启示录1 小时前
Apache Kafka Consumer 扩到 40 个仍不提速:Partition 上限锁死有效并行度 【Kafka合集】
数据库·经验分享·分布式·缓存·面试·kafka·apache
数智启示录1 小时前
Apache Kafka 幂等 Producer 的边界:Exactly-Once 到了 MySQL 为什么失效 【Kafka合集】
数据库·经验分享·分布式·mysql·面试·kafka·apache
Java小白笔记1 小时前
Java 实现阿里云 OSS 文件上传链路:普通上传、秒传、分片与断点续传
java·开发语言·数据库·spring·阿里云
—Miss. Z—1 小时前
计算机三级数据库技术—填空题
数据库·mysql
数智启示录1 小时前
Apache Kafka 同 Key 仍会乱序:从分区 Offset 到业务可见的四道边界 【Kafka 合集】
大数据·数据库·经验分享·分布式·面试·kafka·apache
烬羽2 小时前
给 Agent 一个检索式记忆:把对话历史存进 Milvus,该记的都记得
javascript·数据库·agent
星云API技术支持2 小时前
企业微信二次开发外部群:群聊数据与联系人信息如何建立关联
数据库·php·企业微信