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$;
相关推荐
景煜拾年3 小时前
InnoAI SQL 助手
运维·数据库·笔记·sql
.痞老板.3 小时前
mysql初阶--数据类型
android·数据库·mysql
Risehuxyc4 小时前
MySQL密码插件
数据库·mysql
腾科IT教育4 小时前
Oracle考试全解析:OCP、OCM认证体系与核心考点备考指南
数据库·oracle·开闭原则
叼烟扛炮14 小时前
Linux 学习笔记(五十六):IO 多路复用初探——select 的“三宗罪”
linux·服务器·数据库·笔记·学习·php
.痞老板.16 小时前
mysql初阶--数据库基础
数据库·mysql
城北徐宫18 小时前
MySQL约束条件一览
数据库·mysql
ClouGence20 小时前
数据库变更审批困局,被这个开源工具解决了
数据库·开源
TDengine (老段)20 小时前
TDengine IDMP 十大实用功能(9)
大数据·数据库·人工智能·物联网·时序数据库·tdengine·idmp
starrocks_stella21 小时前
EMR Serverless StarRocks Object Table:用 SQL 连接 OSS 文件与多模态 AI
数据库