【问题标题】:PostgreSQL: Convert UUID into OIDPostgreSQL:将 UUID 转换为 OID
【发布时间】:2021-05-19 10:28:59
【问题描述】:

PostgreSQL 中是否有将 UUID (RFC 4122) 转换为 OID (ISO 8824) 的函数?

“2.25”之后的值。是将 UUID 直接十进制编码为整数。它必须是单个整数的直接十进制编码,全部为 128 位。它不能被分解成部分。

例如,该函数将采用 UUID "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" 并返回 OID "2.25.329800735698586629295641978511506172918"

参考:

【问题讨论】:

    标签: postgresql types casting uuid


    【解决方案1】:

    理想情况下,我们应该有一个无符号的 16 字节整数 (uint16) 和一个注册转换 uuid --> uint16(内部可能兼容也可能不兼容二进制,因此非常便宜) .这些都没有在股票 PostgreSQL 中实现。

    您可以查看(非官方!)附加模块 pg_bignumEvan Caroll's (even more unofficial) fork 直接接受十六进制输入。 (免责声明:未经测试。)

    这些模块在大多数托管安装中不可用。这是一个使用标准 PostgreSQL 内置工具的穷人实现

    CREATE OR REPLACE FUNCTION f_uuid2oid(_uuid uuid)
      RETURNS text
      LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
    $func$
    SELECT '2.25.' ||
           ('x0'      ||       left(hex, 15)     )::bit(64)::int8 * numeric '295147905179352825856' -- 1.
         + ('x0'      || right(left(hex, 30), 15))::bit(64)::int8 * numeric '256'                   -- 2.
         + ('x000000' ||       right(hex,  2)    )::bit(32)::int4                                   -- 3.
    FROM   translate(_uuid::text, '-', '') t(hex)
    $func$;
    
    COMMENT ON FUNCTION public.f_uuid2oid(uuid) IS '
    Convert UUID (RFC 4122) into a OID (ISO 8824) ?
    First, get text representation of UUID without hyphens:  
      translate(_uuid::text, '-', '')`
    Then:
    1.
    - take the first 15 hex digits
    - prefix with x0
    - cast to bit(64)
    - cast to int8
    - multiply with numeric 295147905179352825856 (= 2^68), which is the same as left-shift the binary representation by 68 bits.
      68 bits because: 1 hex digit represents 4 bit; uuid has 128; 128 - 15*4 = 68; so shift by 68
    2.  
    - take the next 15 hex digits
    - prefix with x0
    - cast to bit(64)
    - cast to int8
    - multiply with numeric 256 (= 2^8) shift by the remaining 2 hex digits / 8 bit
    3. 
    - take the remaining, rightmost 2 hex digits
    - prefix with x000000
    - cast to bit(32)
    - cast to int4
    
    Add 1. + 2. + 3., convert to text, prefix "2.25." Voila.
    No leading zeros, according to https://www.rfc-editor.org/rfc/rfc3061
    
    More explanation:
    - https://stackoverflow.com/questions/8316164/convert-hex-in-text-representation-to-decimal-number/8335376#8335376
    - https://dba.stackexchange.com/questions/115271/what-is-the-optimal-data-type-for-an-md5-field/115316#115316
    ';
    

    呼叫:

    SELECT f_uuid2oid('f81d4fae-7dec-11d0-a765-00a0c91e6bf6');
    

    产生请求的 OID 2.25.329800735698586629295641978511506172918

    db小提琴here

    没有前导零,根据https://www.rfc-editor.org/rfc/rfc3061
    我没有通读所有各种标准:http://www.oid-info.com/faq.htm#1

    我尽我所知优化了性能,利用从bit(n)bigint /integer 的内置(非常快)二进制强制转换。要了解我在那里做什么,请先阅读:

    Postgres 整数类型是有符号的。所以 - 为了避免溢出到负数 - 我们不能使用完整的 64 位(8 字节 / 16 个十六进制数字),我们必须将 32 个十六进制数字转换为三个垃圾而不是两个。我任意将其分割为 15 + 15 + 2 个十六进制数字。

    使用left()right() 通常比substring() 快一点。

    还要考虑函数的注释。

    【讨论】:

      猜你喜欢
      • 2014-09-13
      • 2020-01-20
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2018-03-08
      • 2011-03-28
      相关资源
      最近更新 更多