You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PostgreSQL中生成指定范围内的加密安全随机整数?

问题描述

我希望编写一个PL/pgSQL函数,用于生成指定范围内的加密安全随机整数。

背景信息

许多应用层编程语言都提供了生成加密安全随机整数的内置函数,这类函数可用于生成密钥或验证码。比如Node.js和Python都有对应的实现方法。但PostgreSQL没有内置此类函数。

实现难点

合理的实现必须避免引入模偏倚(modulo bias),否则生成的整数可能分布不均。StackOverflow上部分相关PostgreSQL解决方案未考虑该问题。

尝试实现

我基于Node.js的crypto.randomInt函数实现编写了以下函数,pgTAP测试已通过,且我通过生成200万次不同范围内的随机整数并绘图验证,未发现模偏倚。但我仍需谨慎确认是否存在疏漏或错误。

以下函数是否需要调整?它接收两个整数参数low和high,返回[low, high)范围内的随机整数。

CREATE EXTENSION pgcrypto;

CREATE FUNCTION random_int(low INT, high INT)
    RETURNS INT
    LANGUAGE plpgsql
    STRICT
    AS $$
DECLARE
    -- 4字节有符号整数的最大值
    RAND_MAX INT := 2147483647; 
    rand_range INT;
    rand_limit INT;
    rand_bytes BYTEA;
    x INT;
BEGIN
    IF low >= high THEN
        RAISE EXCEPTION 'Low must be less than high';
    END IF;

    -- 可生成的随机整数范围
    rand_range := high - low;

    -- 能被rand_range整除的最大值
    rand_limit := RAND_MAX - (RAND_MAX % rand_range);

    LOOP
        -- 生成4个随机字节
        rand_bytes := gen_random_bytes(4);

        -- 将4个随机字节转换为4字节有符号整数,屏蔽符号位确保为正整数
        -- 127的二进制为'01111111',按位与操作会丢弃第一个字节的符号位
        x := ((get_byte(rand_bytes, 0) & 127) << 24)
            | (get_byte(rand_bytes, 1) << 16)
            | (get_byte(rand_bytes, 2) << 8)
            | get_byte(rand_bytes, 3);

        -- 仅接受小于rand_limit的随机整数以避免模偏倚
        -- 我们接受小于rand_limit的值,确保x % rand_range分布均匀
        -- 任何x >= rand_limit时,x % rand_range会存在偏倚
        -- 举个简化示例:rand_range = 5,RAND_MAX = 12,rand_limit = 12 - (12 % 5) = 10
        -- x             : | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10       | 11        | 12       |
        -- x % rand_range: | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 | 4 | 0 (偏倚) | 1  (偏倚) | 2 (偏倚) |
        -- 此时0、1、2出现的概率高于3和4
        -- 为避免偏倚,我们只接受x < rand_limit的情况
        -- x             : | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
        -- x % rand_range: | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 | 4 |
        -- 加上low确保随机整数落在[low, high)范围内
        IF x < rand_limit THEN
            RETURN (x % rand_range) + low;
        END IF;
    END LOOP;

    -- 若rand_range超过4字节有符号整数的表示范围,PostgreSQL会抛出整数越界错误
    -- 捕获该错误并抛出更明确的异常
    EXCEPTION WHEN numeric_value_out_of_range THEN
        RAISE EXCEPTION 'The difference between high and low cannot exceed the maximum value of a 4-byte signed integer';
END;
$$;
测试结果
SELECT random_int(1, 4);

-- 返回[1, 4)范围内的随机整数
SELECT random_int(-4, 1);

-- 返回[-4, 1)范围内的随机整数
SELECT random_int(-2, 2);

-- 返回[-2, 2)范围内的随机整数
SELECT random_int(0, 2147483647);

-- 返回[0, 2147483647)范围内的随机整数
SELECT random_int(-1, 2147483647);

-- 抛出异常
-- (high - low) 超过4字节有符号整数的最大值

内容的提问来源于stack exchange,提问作者user23454440

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 13:06:04