【问题标题】:Convert PostgreSQL bytea-stored serialized-java-UUID to postgresql-UUID将 PostgreSQL bytea 存储的 serialized-java-UUID 转换为 postgresql-UUID
【发布时间】:2014-09-13 00:32:36
【问题描述】:

我们的一个软件项目使用 PostgreSQL 表,其中列 'guid' 类型为 bytea。

这与带有 PostgreSQL 8.4 的 hibernate 3.3.2.GA 一起使用,它使用 java object serialization 序列化 java UUID 类型。结果是类似于以下escape 格式字节文字的值:

'\254\355\000\005sr\000\016java.util.UUID\274\231\003\367\230m\205/\002\000\002‌​J\000\014leastSigBitsJ\000\013mostSigBitsxp\273\222)\360*r\322\262u\274\310\020\3‌​42\004M '

...我们不能轻易地在查询中用作选择或条件来检索相关行。

有没有人可以在不设置休眠查询的情况下读取或使用查询的 select 部分或 where 部分中的 bytea 列(例如通过 psql 或 pgadmin3)?

【问题讨论】:

    标签: java postgresql serialization uuid bytea


    【解决方案1】:

    更新:见问题编辑,这个答案适用于常见的 16 字节序列化 uuid;修改了问题以反映 java 序列化


    有趣的问题。我开始编写一个简单的 C 扩展来高效地完成它,但使用下面的 PL/Python 版本可能更明智。

    因为uuid 是固定大小的类型,而byteavarlena,你不能只用create cast ... as implicit 对它们进行二进制强制,因为可变长度的字段标头会妨碍它们。

    bytea 输入没有返回 uuid 的内置函数。拥有它会很方便,但我认为还没有人做过。

    最简单的方法

    更新:实际上有一个简单的方法可以做到这一点。一旦\x 被剥离,十六进制形式的bytea 实际上是一个有效的uuid 文字,因为uuid_in 接受没有-{} 的普通未修饰的十六进制。所以只是:

    regress=> SET bytea_output = 'hex';
    SET
    regress=> SELECT CAST( substring(CAST (BYTEA '\x0FCC6350118D11E4A5597DE5338EB025' AS text) from 3) AS uuid);
                  substring               
    --------------------------------------
     0fcc6350-118d-11e4-a559-7de5338eb025
    (1 row)
    

    它涉及几个字符串副本和一个十六进制编码/解码周期,但它比我之前建议的任何 PL 答案都要快很多,尽管比 C 慢。

    其他选项

    我个人建议使用 PL/Perl 或 pl/pythonu。我会跟进一个例子。

    假设您的 uuid 是十六进制格式的 bytea 文字:

    '\x0FCC6350118D11E4A5597DE5338EB025'
    

    你可以把它变成 uuid 类型:

    PL/Perl

    create language plperlu;
    
    create or replace function to_uuid(bytea) returns uuid language plperlu immutable as $$
    use Data::UUID;
    my $ug = new Data::UUID;
    my $uuid = $ug->from_hexstring(substr($_[0],2));
    return $ug->to_string($uuid);
    $$
    SET bytea_output = hex;
    
    SELECT to_uuid(BYTEA '\x0FCC6350118D11E4A5597DE5338EB025');
    

    PL/Python

    在 Python 中它可能更快更干净,因为 PL/Python 接口将 bytea 作为原始字节而不是十六进制字符串传递:

    CREATE LANGUAGE plpythonu;
    
    CREATE or replace function to_uuid(uuidbytes bytea) 
    RETURNS uuid LANGUAGE plpythonu IMMUTABLE 
    AS $$
    import uuid
    return uuid.UUID(bytes=uuidbytes)
    $$;
    
    SELECT to_uuid(BYTEA '\x0FCC6350118D11E4A5597DE5338EB025');
    

    在 C 中,只是为了好玩。丑陋的黑客。

    可以看到C扩展模块here

    但实际上,我的意思是它很丑。如果您希望它在 C 中正确完成,最好实际修补 PostgreSQL 而不是使用扩展。

    【讨论】:

    • 也许我应该更清楚地提到 bytea 列的内容是 UUID 实例的 java 序列化版本,例如:select guid from documents limit 1;"\254\355\000\005sr\000\016java.util.UUID\274\231\003\367\230m\205/\002\000\002J\000\014leastSigBitsJ\000\013mostSigBitsxp\273\222)\360*r\322\262u\274\310\020\342\004M "
    • @FvHovell 呃,是的。至少可以说这很有用。这就是为什么您应该始终包含示例数据
    【解决方案2】:

    经过反复试验,我创建了以下函数来提取 postgresql-UUID 值:

    CREATE OR REPLACE FUNCTION bytea2uuid (x bytea) RETURNS uuid as $$ SELECT encode(substring(x, 73, 8) || substring(x, 65, 8), 'hex')::uuid $$ language sql;

    这通过提取用于最小SigBits 和mostSigBits(以相反的顺序存储)的Java 长值中使用的字节,而不是编码为十六进制并转换为类型“uuid”。

    使用如下: select bytea2uuid(guid) as guid from documents limit 1;

    "75bcc810-e204-4d20-bb92-29f02a72d2b2"

    【讨论】:

    • 你最好也验证一下 serialVersionUid 字段,确保你没有解码错误。
    • 您在一般情况下是正确的,但出于我的目的,我确信所有 UUID 序列化都是使用 java 6 执行的,因为它用于我们所有的项目。因此,在我的情况下,我不需要检查 serialVersionUid,因为保证所有 guid 值都是相同的。
    【解决方案3】:

    这对我有用:

    ALTER TABLE myTable ALTER COLUMN id TYPE uuid USING CAST(ENCODE(id, 'hex') AS uuid);
    

    【讨论】:

    • 询问的编码不正确:一个 java.util.UUID 序列化值,例如:“\254\355\000\005sr\000\016java.util.UUID\274\231\ 003\367\230m\205/\002\000\002‌​J\000\014leastSigBitsJ\000\013mostSigBitsxp\273\222)\360*r\322\262u\274\310\020\3‌​42\004M "
    猜你喜欢
    • 2021-05-19
    • 2018-03-08
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多