对象模态

九有数据库UNVDB 融合对象存储UDB-TO

一种安全、廉价、方便的文件读写方法

(V1.1.0)

一、系统概述

痛点1:数据库为追求性能,存储介质价格高昂,不适合存储低频大文件。

痛点2:私密数据如果泄漏,将会带来不可预料的后果。

痛点3:自己开发压缩、加密方法有一定成本。

痛点4:文件和业务数据是分别存储的,需要自己建立关联关系。

为解决这些问题,UnvDB推出全新文件读写方式:

写文件 读文件
1. 文件和业务数据一起写入UnvDB中 1. 从UnvDB中检索对象存储路径
2. UnvDB自动压缩并加密文件 2. 从对象存储中读取加密文件
3. 将加密文件写入廉价对象存储 3. UnvDB自动解密并解压文件
4 .UnvDB中只记录对象存储路径 4. 解密后的文件和业务数据一起返回

这种文件读写方式在业务层看来跟日常访问文件一样,处理的是明文。而实际文件已经压缩并加密,并存入廉价对象存储。不仅增强安全性,而且减少存储成本和开发成本,文件和业务数据得到很好的关联。加密的文件即使被黑客盗取,也无法看到明文。从而实现安全、廉价、方便地存储文件的目的。

二、环境准备

2.1. 交付包形态与解压

对外发布的 tar.gz 典型文件名形如:

udbto-<操作系统标签>-UNVDB-tx-<版本>-binary-deploy.tar.gz

在任意工作目录执行解压并进入包内顶层目录udbto/, 例如:

tar zxf udbto-openEuler-24.03-UNVDB-tx-24.2-binary-deploy.tar.gz
cd udbto

2.2. 一键安装到当前实例

将 UD_CONFIG 设置为 当前 UNVDB 安装路径下的 ud_config 所在路径后执行安装脚本:

export UD_CONFIG=/path-to-unvdb/bin/ud_config
sudo ./install_binary.sh

安装完成后请 重启数据库,使新装入的共享库与SQL脚本生效。

systemctl restart udbtx.service 
-- 服务文件名udbtx.service根据实际修改

2.3. 在数据库中启用

重启后,在需要使用的 目标数据库 中执行:

-- 创建
CREATE EXTENSION udbto;

2.4. 设置URL和TO相关访问凭证

  • 调用 udbto_set_url(url1, url2, url3) 配置一至三个可轮换的端点;至少保证有一个端点可达。

SELECT udbto_set_url('110.41.19.214:15410', '', ''); // 三个URL只要有一个可用即可

设置TO相关访问凭证: SELECT udbto_set_key('GpUzPwYOJhVD3gMw', '771cLzhrDOhJgHCASlMk0C1pW4quAlzY');

  • SECRET_ACCESS_KEY 在库内以密文形式保存。

SELECT udbto_set_key('GpUzPwYOJhVD3gMw', '771cLzhrDOhJgHCASlMk0C1pW4quAlzY');

三、快速使用

3.1. 不加密版使用

3.1.1 创建普通桶:

  • 参数: ‘普通桶名称’

SELECT udbto_bucket_create('demo-bucket');

3.1.2 上传/下载

未配置钱包桶时不能使用加密/压缩,此时上传/下载, 按 明文(plain) 处理。 上传接口udbto_object_put参数说明:

  • 第一参数为上传内容在TO中的s3路径(s3://桶名/文件名, 文件名用户自定义);

  • 第二参数为 bytea;

  • 第三参数 info 可选,默认为空字符串

注意: 本插件的上传和下载接口为统一接口, 下述示例不再重复解释参数含义

SELECT udbto_object_put(
  's3://demo-bucket/hello.txt',
  convert_to(repeat('x', 128*1024*1024), 'UTF8'),  -- 转为二进制格式(bytea)
  'mode is plain'
);
      udbto_object_put      
----------------------------
 s3://demo-bucket/hello.txt
(1 row)

– 这里用到的 convert_to(…)和下文的convert_from都是为了测试使用方便显示而已

– 下载udbto_object_get(’s3路径’);

SELECT convert_from(udbto_object_get('s3://demo-bucket/hello.txt'), 'UTF8');
        convert_from         
-----------------------------
 Hello from UDBTO plain mode
(1 row)

3.1.3 删除指定对象:

SELECT udbto_object_delete('s3://demo-bucket/hello.txt');

3.2. 钱包压缩加密版使用

3.2.1 一般操作流程

  • 创建钱包;

  • 打开钱包;

  • 创建钱包桶

    • 此时会通过参数mode指定钱包桶内数据在TO的存储形式: lz4 / aes / sm4 或混合 lz4|aes、lz4|sm4;

  • 打开钱包桶;

  • 进行上传/下载操作

    • 会按照创建钱包桶时指定的mode, 自动完成上传时压缩/加密, 下载时解密/解压操作;

3.2.2 创建并打开钱包

TO连接与密钥已配置, 同上略。

创建钱包 udbto_wallet_create(wallet_password text),请用户自行保存好钱包密码;若钱包已存在,接口报错 MASTER WALLET ALREADY EXISTS。需改密码请用 udbto_wallet_password_change(旧, 新)。

SELECT udbto_wallet_create('udbto123');

打开钱包 udbto_wallet_open(wallet_password text), 若本会话内钱包重复打开:

  • 若密码与库内一致则返回 true 并输出 NOTICE: UDBTO: master wallet already OPEN in this session; password verified (no state change);

  • 若密码错误则仍报错 MASTER WALLET PASSWORD ERROR(28P01),与首次打开时密码错误相同。

SELECT udbto_wallet_open('udbto123');

关闭钱包 udbto_wallet_close(), 若本会话内钱包重复关闭: 返回 true 并输出 NOTICE(幂等)。

注意相关钱包桶操作一般需要先打开钱包

下面将分别用 加密模式aes和sm4, 压缩模式lz4, 压缩和加密混合模式lz4|aes和lz4|sm4进行演示

3.2.3 使用混合lz4|sm4模式

– 创建钱包桶之前需要先打开钱包, 错误示例如下:

SELECT udbto_wallet_bucket_create('lz4-sm4-demo-bucket', 'VerifyKey123', '1234567890ABCDEF', 'lz4|sm4');
ERROR:  UDBTO:internal: MASTER WALLET NOT OPEN

– 创建钱包桶:

创建钱包桶接口 udbto_wallet_bucket_create(bucket_name, bucket_password, bucket_file_key, mode) -> bool。

参数解释:

  • bucket_name: 钱包桶名称

  • backet_password: 钱包桶密码

  • bucket_file_key: 钱包桶内用于文件加密的秘钥

  • mode: 加密/压缩模式, 如: aes, sm4, lz4, lz4|aes, lz4|sm4等

注意事项:

  • 创建钱包桶要求钱包已打开

  • 重复 udbto_wallet_bucket_create 会报错 BUCKET WALLET ALREADY EXISTS(不会静默成功、也不会用新参数覆盖旧密钥);

  • 修改密码请用 udbto_wallet_bucket_password_change。

SELECT udbto_wallet_bucket_create('lz4-sm4-demo-bucket', 'VerifyKey123', '1234567890ABCDEF', 'lz4|sm4');

– 打开钱包桶:

参数解释:

  • wallet_bucket_name: 钱包桶名称

  • wallet_bucket_password: 钱包桶密码

select udbto_wallet_bucket_open('lz4-sm4-demo-bucket', 'VerifyKey123'); 

若本会话内该钱包桶重复打开:

  • 密码正确则 NOTICE 幂等返回 true;

  • 密码错误仍报 BUCKET VERIFY KEY ERROR;

udbto_wallet_bucket_close 在会话内该钱包桶重复关闭,会 NOTICE 提示。

– 上传

  • 会自动按照创建钱包桶时指定的mode去上传/下载;

  • 加密/压缩数据的上传/下载需要先打开钱包桶;

SELECT udbto_object_put(                 
  's3://lz4-sm4-demo-bucket/hello.txt',
  convert_to('Hello from UDBTO lz4|sm4 mode', 'UTF8'),
  'mode is encrypt'
);
        udbto_object_put        
--------------------------------
 s3://lz4-sm4-demo-bucket/hello.txt
(1 row)
此时完成lz4压缩和sm4加密(按创建钱包桶时指定mode), 并把压缩和加密后的内容上传到TO侧

– 下载

SELECT convert_from(udbto_object_get('s3://lz4-sm4-demo-bucket/hello.txt'), 'UTF8');
       convert_from        
---------------------------
 Hello from UDBTO lz4|sm4 mode
(1 row)
此时自动完成文件的下载和解密, 通过convert_from转码为UTF8便于展示原明文内容

– 删除对象(这里的对象指的是TO侧保存的具体文件)
注意: 在创建钱包桶并指定了mode为加密/压缩/加密压缩的混合模式后, 删除该钱包桶的某个对象需要先打开钱包桶;

select * from udbto_object_delete('s3://lz4-sm4-demo-bucket/hello.txt');

3.2.4 使用其他模式

– 创建aes钱包桶(目前aes模式只支持128位, CBC分组, mode输入: aes_128_cbc, 也可以缩写为aes)

SELECT udbto_wallet_bucket_create('aes-demo-bucket', 'VerifyKey123', '1234567890ABCDEF', 'aes');

– 创建lz4钱包桶:

SELECT udbto_wallet_bucket_create('lz4-demo-bucket', 'VerifyKey123', '1234567890ABCDEF', 'lz4');

– 创建lz4|aes钱包桶:

SELECT udbto_wallet_bucket_create('lz4-aes-demo-bucket', 'VerifyKey123', '1234567890ABCDEF', 'lz4|aes');

注意事项:

  • 使用不同mode只需改变最后一个参数即可

  • 其他操作: 打开钱包桶, 上传/下载 使用接口和流程同上, 不再叙述

3.3. 与业务表联用(以文章表举例, 钱包桶lz4|sm4压缩加密模式)

  1. 创建表

 -- ========== 1. 业务表:只存「对象路径」关联 ==========
 CREATE TABLE articles (
   article_name text,
   article_file text   -- 存 s3://... 路径,不存 bytea 正文
 );
  1. 依次创建并打开钱包 和 钱包桶(lz4|sm4压缩加密模式), 命令同上

  2. 插入数据

 -- ========== 写入:put 返回路径落入 article_file ==========
 INSERT INTO articles (article_name, article_file)
 VALUES (
   'article_name1',
   udbto_object_put(
     's3://lz4-sm4-demo-bucket/article_name1.txt',
     convert_to('Hello from UDBTO lz4|sm4 mode', 'UTF8'),
     'article demo'              -- info 可选,可 NULL/省略
   )
 );
  1. 查询数据

 -- ========== 读取:用表中路径 get,再转 UTF8 展示 ==========
 SELECT
   a.article_name,
   convert_from(udbto_object_get(a.article_file), 'UTF8') AS body_utf8
 FROM articles a
 WHERE a.article_name = 'article_name1';

四、系统详细功能

本章从业务与运维用户视角,补充 第一章「系统概述」:说明在数据库里用 UDB-TO 能完成哪些事、有哪些使用边界,以及产品为何在常见威胁模型下更安全、更可控。

4.1 UDB-TO功能全景

  • 对接对象存储:在库内登记一至多个可轮换的访问端点,并登记对象存储访问密钥,业务 SQL 只需关心「路径 + 数据」。

  • 普通桶与对象(基础能力):创建/删除TO桶(在符合「空桶」等前提时)、按统一路径格式上传与下载对象、删除对象;可按需要走整对象上传/下载,也可对大对象使用分段上传与分段读取以控制单次传输体量(具体接口见第五章);适合对对象正文不做压缩/加密的场景。此时对象在存储侧按明文语义读写。

  • 钱包(可选的一层总控):为UDB-TO敏感操作提供主密码保护。启用后,与「钱包桶」相关的创建、打开、改密、删除等,通常要求本会话已打开钱包,避免任意数据库用户仅凭一条 SQL 就改动所有钱包桶的保护策略。

  • 钱包桶(对TO桶加固):为某一个TO桶单独设定验证密码、文件侧密钥材料与处理模式(压缩、加密或组合)。启用后,对该TO桶的读写可按策略做透明压缩/加密;未在本会话打开对应钱包桶时,不能正确读写已受保护的对象。

4.2 术语

说法 含义
TO / UDB-TO / UDBTO TO是简称,UDB-TO是全称,UDBTO是全称的连体写法, 都指代本产品
TO桶 对象存储中的桶;在 UDB-TO 中对应「一个被登记、可被访问的桶名空间」。
普通桶 UNVDB数据库中用于存储明文数据的桶, 与远端的TO桶一一对应
钱包 一层总密码保护:用于约束「谁能在本会话里动钱包桶相关敏感操作」。
钱包桶 UNVDB数据库中用于存储加密/压缩数据的桶;与远端的TO桶一一对应
钱包桶模式(mode) 对该TO桶对象正文如何处理:plain(明文)、lz4(压缩)、aes / sm4(加密)及组合等。
S3 Amazon Simple Storage Service(简单存储服务), 一种对象存储协议

4.3 钱包与钱包桶如何协同(用户需要记住的规则)

  1. 钱包:打开钱包,表示本会话已被授权处理「受钱包约束」的钱包桶类操作;关闭或断线后,这一授权不延续到别的连接。

  2. 钱包桶:每个钱包桶可以有自己的密码与策略;即使钱包已打开,对某个加密/压缩钱包桶读写前,通常仍需再打开钱包桶。这样即使多人共用同一数据库账号,不知道钱包桶密码仍无法操作受保护的钱包桶内的对象。

  3. 对象仍在UDB-TO:数据库侧主要保存登记与保护元数据;文件本体(对象)存储在UDB-TO。

4.4 钱包桶模式说明

写法大小写不敏感;产品会把等价写法归一成统一形式(例如加密算法在文档与界面中常体现为带下划线的规范名)。

  • plain:不在钱包桶侧做压缩/加密编排(等价于普通桶),对象按明文语义存放与读取。

  • lz4:钱包桶侧压缩,减小占用与传输量;读取侧需能按同一规则解压。

  • aes / sm4**:钱包桶侧单算法加密;当前产品对外说明为 **128 位密钥、CBC 分组语义(名称上常写作 aes_128_cbc、sm4_128_cbc 一类)。其它强度/模式以后续版本说明为准。

  • lz4|aes、lz4|sm4 等:先压缩再加密(或按产品约定的管道顺序),兼顾体积与机密性。

启用钱包桶后,钱包桶的处理方式与密钥相关信息会以加密形式保存在库内,而不会长期以「谁都能读懂的明文」散落在配置里——从而降低「备份泄露即策略与密钥材料一并裸奔」的风险。

4.5 安全性说明

  • 「分桶分钥」降低牵连面:一个钱包桶的密码或材料泄露,不影响其他钱包桶;在钱包桶之上钱包又提供一层保护,避免任意会话随意访问钱包桶。

  • 「打开」绑定会话,缩短暴露窗口:敏感能力默认要求显式打开;连接归还连接池、会话结束后,解密能力不可用。对攻击者而言,拿到磁盘备份或只读导出,仍不能在没有密码与会话授权链的情况下完成解密读写。

  • 「密文落库」减少拖库损失:对象存储访问密钥、钱包桶侧敏感材料等按产品设计以密文或受保护形式登记;即便数据库被非法拷贝,攻击面也明显小于「明文 AK/SK、明文策略、明文密钥材料」堆在同一库中的方案。

  • 重要提醒(用户侧必须做到位):安全是系统工程。请妥善保管钱包密码、钱包桶密码,控制数据库与存储的账号权限与网络边界。

4.6 注意事项

  • 会话与连接池:不同连接之间的钱包/钱包桶打开状态互不影响;从池中取连接后,按业务需要重新执行打开顺序(钱包 → 相关钱包桶)。

  • 删空桶:删除普通桶/钱包桶前需保证桶已空(使用udbto_object_delete会同时删除数据库内元数据与TO桶中的对象);

  • 参数大小限制: S3 路径结构是 s3://<桶名>/<对象键>, 其中桶名长度范围3~63字节, 对象键长度小于等于746字节

  • 创建桶: 创建普通桶/钱包桶成功后, 需要等待约十几秒才能在桶中上传对象, 因为对象存储采用分布式结构, 数据节点和主节点之间需要时间同步, 在此期间上传对象会报错 UDBTO PUT OBJECT ERROR, 稍后重试上传即可.


五、接口文档

5.1 连接与密钥

接口 功能说明 参数说明
udbto_set_url(url1 text, url2 text, url3 text) -> bool 写入对象存储连接信息(会保存在udbto_ns.udbto_info表中) url1:第一个端点地址 url2:第二个端点地址(可为空字符串) url3:第三个端点地址(可为空字符串) 三个参数中至少保证有一个端点可达即可
udbto_set_key(ACCESS_KEY_ID text, SECRET_ACCESS_KEY text) -> bool 写入 AK(ACCESS_KEY_ID), SK(SECRET_ACCESS_KEY),TO侧的访问凭证(会保存在udbto_ns.udbto_info表中) ACCESS_KEY_ID:TO 访问密钥 ID SECRET_ACCESS_KEY:TO 访问密钥(库内以密文登记)

5.2 普通桶

接口 功能说明 参数说明
udbto_bucket_list() -> table(...) 仅列举本地 udbto_ns.udbto_info 中登记的 普通桶(bucket_mode:<桶名>=plain)。加密钱包桶侧清单见 udbto_wallet_bucket_list。 无
udbto_bucket_create(bucket_name text) -> bool 在对象存储创建TO桶登记 明文 bucket_mode;若该TO桶已存在 钱包桶(加密)元数据则拒绝。 bucket_name:普通桶名称
udbto_bucket_delete(bucket_name text) -> bool 只能用于删除普通桶。使用前请确保普通桶内没有对象 bucket_name:普通桶名称

5.3 钱包

接口 功能说明 参数说明
udbto_wallet_create(wallet_password text) -> bool 仅首次:写入钱包元数据;库中已有钱包时报错 MASTER WALLET ALREADY EXISTS。改密见 udbto_wallet_password_change wallet_password:访问钱包的密码
udbto_wallet_open(wallet_password text) -> bool 打开钱包。本会话已为 OPEN 时再次 open:先校验密码,正确则返回 true 并打印 NOTICE 提示;密码错误仍报 MASTER WALLET PASSWORD ERROR(28P01)。 wallet_password:访问钱包的密码
udbto_wallet_close() -> bool 关闭钱包并清空本会话钱包桶密钥缓存。本会话已为 CLOSE 时再次 close:返回 true 并 打印 NOTICE 提示。 无
udbto_wallet_state() -> text OPEN / CLOSE 无
udbto_wallet_password_change(old_password text, new_password text) -> bool 修改钱包密码;不要求当前会话已 open,但必须提供正确 旧密码 old_password:旧钱包密码 new_password:新钱包密码

5.4 钱包桶

接口 功能说明 参数说明
udbto_wallet_bucket_create(bucket_name, bucket_password, bucket_file_key, mode) -> bool 创建钱包桶;要求钱包已打开;若对象存储侧尚无该TO桶则 自动创建 TO桶,再写入加密元数据。重复创建时报错 BUCKET WALLET ALREADY EXISTS。 bucket_name:钱包桶名称 bucket_password:打开钱包桶的密码 bucket_file_key:文件加密使用的密钥 mode:钱包桶模式(如 aes、sm4、lz4 及压缩与加密组合写法,见第三章)
udbto_wallet_bucket_open(bucket_name, bucket_password) -> bool 打开钱包桶。本会话内该钱包桶重复打开:先校验钱包桶密码,正确则返回 true 并 打印 NOTICE 提示;密码错误仍报 BUCKET VERIFY KEY ERROR(28P01)。 bucket_name:钱包桶名称 bucket_password:钱包桶密码
udbto_wallet_bucket_close(bucket_name) -> bool 关闭该钱包会话密钥。本会话该钱包桶已为 CLOSE 时再次 close:返回 true 并 打印 NOTICE 提示。 bucket_name:钱包桶名称
udbto_wallet_bucket_state(bucket_name) -> text 会话内该钱包桶是否已打开。 bucket_name:钱包桶名称
udbto_wallet_bucket_delete(bucket_name, bucket_password) -> bool 删除钱包桶;要求钱包已通过 udbto_wallet_open 打开;不要求本会话已 udbto_wallet_bucket_open;与 udbto_bucket_delete 要求相同:须为空桶。 bucket_name:钱包桶名称 bucket_password:钱包桶密码
udbto_wallet_bucket_list() -> table(...) 钱包打开时可调用;mode 为英文类别 无
udbto_wallet_bucket_password_change(bucket_name, old_bucket_password, new_bucket_password) -> bool 修改钱包桶密码;要求 钱包已打开,不要求钱包桶已打开;必须提供正确旧钱包桶密码。 bucket_name:钱包桶名称 old_bucket_password:旧钱包桶密码 new_bucket_password:新钱包桶密码

5.5 对象上传 / 下载 / 删除

接口 功能说明 参数说明
udbto_object_put(path text, data bytea, info text DEFAULT NULL) -> text 按钱包桶模式处理对象,随后上传TO侧。 path:s3:// 开头的对象路径(含桶名与对象名) data:待上传的二进制内容(bytea) info:可选说明文本
udbto_object_get(path text) -> bytea 按钱包桶模式读取。 path:s3:// 开头的对象路径(含桶名与对象名)
udbto_object_delete(path text) -> bool 删除对象;若钱包桶为非明文模式,须本会话已 udbto_wallet_bucket_open。 path:s3:// 开头的对象路径(含桶名与对象名)

5.6 分段上传 / 下载

接口 功能说明 参数说明
udbto_object_put_part_start(path text, info text DEFAULT NULL) -> text 开始 multipart;info 可选。 path:s3:// 开头的对象路径 info:可选说明(可为 NULL)
udbto_object_put_part_ongoing(path text, part_data bytea) -> text 上传分片。 path:s3:// 开头的对象路径 part_data:本分片的二进制内容(bytea)
udbto_object_put_part_end(path text) -> integer udbto_object_put_part_abort(path text) -> bool 结束分段上传:成功返回 合并后对象总字节数;放弃上传返回 bool。 path:s3:// 开头的对象路径
udbto_object_get_part(path, range_start, byte_length) -> bytea 分段读:第三参为 长度(从 range_start 起连续读取的字节数);若 range_start + byte_length 超出对象大小则报错(SQLSTATE 见服务端)。 path:s3:// 开头的对象路径 range_start:起始字节偏移(如 0) byte_length:要读取的字节长度

分段接口暂时只有非加密版本, 向加密桶中分段上传/下载会报错

何时使用分段上传 / 下载(大小与选型)

以下为 UDBTO 分段操作的常见约定

  • 整对象上传 / 下载(udbto_object_put / udbto_object_get):TO侧单次普通上传对象大小常见上限为 5GB;但是受UNVDB内 palloc 的单次分配上限1GB限制, 实际单次上传对象大小应小于1GB(精确值为1GB-4B), 小于该上限的对象可直接整段读写。

  • 何时优先用分段上传(udbto_object_put_part_*):对象接近或超过单次普通上限、或体积较大希望分片传输以降低单次失败成本与内存占用;常见实践中超过约 100MB 即可考虑 multipart(视网络与稳定性而定)。

  • Multipart 上传的常见量级:TO侧多次分片上传对象的总大小最大约 5TB**;**单次上传对象大小应小于1GB, 除最后一片外,单片常见 ≥5MB。

  • 何时用分段下载(udbto_object_get_part):只需读取对象的某一字节区间,无需一次下载全对象。

使用示例

使用示例(第一片 ≥ 5MiB + 第二片短串):

-- 开始分段上传
SELECT udbto_object_put_part_start('s3://demo-bucket/mp-5m-plus', NULL);
 udbto_object_put_part_start 
-----------------------------
 s3://demo-bucket/mp-5m-plus
(1 row)

-- 第一片:5MiB(ASCII,长度即字节数)
demo=# SELECT udbto_object_put_part_ongoing(
  's3://demo-bucket/mp-5m-plus',
  convert_to(repeat('A', 5 * 1024 * 1024), 'UTF8')
);
     udbto_object_put_part_ongoing     
---------------------------------------
 ,1,"b8fc857a25e7958868c2f003d5e0952d"
(1 row)


-- 第二片:短内容
demo=# SELECT udbto_object_put_part_ongoing(
  's3://demo-bucket/mp-5m-plus',
  convert_to('tail-结尾', 'UTF8')
);
     udbto_object_put_part_ongoing     
---------------------------------------
 ,2,"d77a8175a7676acaf6353ea424ae1ad6"
(1 row)

-- 查看分段表
demo=# select * from udbto_ns.udbto_part;
            path             |                         uploadid                          | partno |                                partresults      
                           
-----------------------------+-----------------------------------------------------------+--------+-------------------------------------------------
---------------------------
 s3://demo-bucket/mp-5m-plus | 41645cfb445a4aa1b6f4008a7485e37fx0342119c17a8b06b04e28bdd |      2 | ,1,"b8fc857a25e7958868c2f003d5e0952d",2,"d77a817
5a7676acaf6353ea424ae1ad6"
(1 row)
-- 结束分段上传
demo=# SELECT udbto_object_put_part_end('s3://demo-bucket/mp-5m-plus');
 udbto_object_put_part_end 
---------------------------
                   5242891
(1 row)

返回值类型为 integer,等于 multipart 完成后对象的总字节数。

-- 分段读(第三参为长度字节数)
-- 读开头 100 字节:起点 0,长度 100
SELECT convert_from(udbto_object_get_part('s3://demo-bucket/mp-5m-plus', 0, 100), 'UTF8');
                                           convert_from                                           
   
----------------------------------------------------------------------------------------------------
---
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
A
(1 row)
-- 读靠近 5MiB 拼接处共 20 字节:起点 总长度5242891-20,长度 20
SELECT convert_from(udbto_object_get_part(
  's3://demo-bucket/mp-5m-plus',
  5242891 - 20,
  20
), 'UTF8');
convert_from  
----------------
 AAAAAtail-结尾
(1 row)

六、常见问题(含表现形式与处理)

6.1 如何查看 SQLSTATE(五位错误码)

UDB-TO 在失败路径使用 UNVDB 服务端通用的 ereport(ERROR, errcode(...), errmsg(...))。客户端拿到的 ErrorResponse 协议消息里 始终带有五位 SQLSTATE;默认交互式 psql 往往只打印 ERROR: 与正文一行,不一定把 SQLSTATE 打在屏幕上。

场景 如何看到 SQLSTATE
ud_sql(UNVDB 自带交互客户端) 每条 SQL 执行后,客户端会维护内置变量::SQLSTATE 对应该条语句结果(成功一般为 00000,失败为五位错误码)。在提示符下执行 \echo :SQLSTATE 即可打印。另可使用 \echo :LAST_ERROR_SQLSTATE、\echo :LAST_ERROR_MESSAGE 查看本会话内最近一次失败语句的状态码与主消息(从未失败过时一般为空消息与 00000,以当前 UNVDB 自带的 psql 帮助文档为准)。\echo :ERROR 表示上一条 SQL 是否失败(true/false)。出错后还可用 \errverbose 查看上一条错误的展开信息,或 \set VERBOSITY verbose / sqlstate 等控制错误展示详细程度(以 psql 文档为准)。
psql + 过程语言块 用 DO $$ ... EXCEPTION WHEN OTHERS THEN ... END $$ 等封装时,可在异常分支读取 SQLSTATE 等诊断变量。
JDBC SQLException.getSQLState() 返回五位字符串。
libpq PQresultErrorField 从查询结果中取出 SQLSTATE 诊断字段。
Python 等驱动 异常对象上通常有五位 SQLSTATE 属性(名称依所用驱动文档为准)。
服务端日志 失败语句对应 ERROR 日志行;提高 log_error_verbosity / log_min_error_statement 等配置后,日志中可出现更完整上下文(与集群策略有关)。

6.2 SQLSTATE 与典型 errmsg

业务失败已按场景映射到标准五位码或 Class **TO9** 自定义码(TO901–**TO919)。客户端看到的 SQLSTATE 与下表一致;**errmsg 仍含接口名与细节,排障时务必读完整消息。

SQLSTATE 典型 errcode(源码宏) 典型 errmsg(节选) 含义与处理
25006 ERRCODE_UDBTO_READ_ONLY_SLAVE IT IS A SLAVE DB, OPERATE ON MASTER DB 只读副本拒绝写;在主库执行配置、对象、分段上传等。
22023 ERRCODE_UDBTO_CONFIG_MISSING / ERRCODE_UDBTO_RANGE_INVALID key is empty / url is empty;range_start and length must be non-negative;range_start+length exceeds object size 未配置 S3 参数或 Range 非法:先 udbto_set_url、udbto_set_key;分段读按第五章校正 udbto_object_get_part。
55000 ERRCODE_UDBTO_WALLET_GATE 等 UDBTO: MASTER WALLET NOT OPEN;master wallet is not open...;UDBTO: BUCKET KEY NOT OPEN, bucket=...;bucket mode is encrypted; open the bucket wallet...;bucket has encrypted wallet metadata...;bucket is not empty / BucketNotEmpty 主/桶钱包未打开、加密门禁、明文桶与钱包桶元数据冲突、删桶非空等:按消息 udbto_wallet_open、udbto_wallet_bucket_open、先清空再删桶等。
55006 ERRCODE_OBJECT_IN_USE 客户端 NOTICE(非 ERROR):master wallet already OPEN...、BUCKET WALLET ALREADY OPEN...、ALREADY CLOSE... 本会话重复 open(密码已校验)或重复 close 的幂等提示;返回值仍为 true。
42710 ERRCODE_UDBTO_DUPLICATE_WALLET UDBTO: MASTER WALLET ALREADY EXISTS;UDBTO: BUCKET WALLET ALREADY EXISTS, bucket=... 主钱包或桶钱包已存在:勿误以为 create 已改密;用 password_change 或受控清理后重建。
42704 ERRCODE_UDBTO_BUCKET_KEY_MISSING UDBTO: BUCKET KEY NOT FOUND, bucket=... 桶密钥密文或元数据缺失:确认已 udbto_wallet_bucket_create。
42P01 ERRCODE_UDBTO_CATALOG_MISSING UDBTO BUCKET LIST QUERY ERROR / UDBTO WALLET BUCKET LIST QUERY ERROR(含 catalog relation missing) 扩展元数据表不可见:执行 CREATE EXTENSION udbto 等(见 errhint)。
42501 ERRCODE_UDBTO_METADATA_PRIV 同上(含 permission denied reading extension metadata) 读 udbto_ns.udbto_info 权限不足:授予 USAGE/SELECT 等。
XX000 ERRCODE_UDBTO_INTERNAL SPI connect fail 等 内部/SPI 连接类失败:查服务端日志与 errdetail。
TO901 ERRCODE_UDBTO_SPI_NON_SELECT_RESULT UDBTO BUCKET LIST QUERY ERROR / UDBTO WALLET BUCKET LIST QUERY ERROR(SPI_execute 非 SPI_OK_SELECT) 列表 SPI 非常规返回:看 errdetail 中 SPI 码。
TO902 ERRCODE_UDBTO_S3_BUCKET_ERROR UDBTO BUCKET LIST S3 ERROR;UDBTO WALLET BUCKET LIST S3 ERROR;UDBTO CREATE BUCKET ERROR;UDBTO DELETE BUCKET ERROR;cannot create or reach bucket on object storage S3/libs3 桶级失败(建/删/清空/列举桶等)。
TO903 ERRCODE_UDBTO_S3_OBJECT_ERROR UDBTO PUT OBJECT ERROR;UDBTO GET OBJECT ERROR;UDBTO DELETE OBJECT ERROR;SET KEY ENCRYPT ERROR;cannot determine object size 等 S3/libs3 对象级失败或全局 SK 加密落库失败(udbto_set_key)。
TO904 ERRCODE_UDBTO_PATH_ERROR path is error;INVALID BUCKET NAME;INVALID WALLET INPUT s3:// 路径或 bucket 名不合法。
TO905 ERRCODE_UDBTO_MULTIPART_ERROR UDBTO PUT PART...;get part info...;get object after multipart... 分段上传、分片元数据或合并后校验失败。
TO906 ERRCODE_UDBTO_METADATA_IO_ERROR MASTER WALLET INFO ERROR 等 udbto_info 等元数据 SPI 读写失败。
TO907 ERRCODE_UDBTO_WALLET_NOT_READY MASTER WALLET NOT CREATED;MASTER WALLET INFO NOT FOUND;RESET CODE LENGTH ERROR 主钱包未创建或重置前置信息缺失。
TO908 ERRCODE_UDBTO_WALLET_RESET_AUTH BUCKET RESET SIGN VERIFY ERROR;BUCKET RESET TARGET MISMATCH 带签名的重置/恢复包验签或目标不匹配。
TO909 ERRCODE_UDBTO_WALLET_OPERATION MASTER WALLET RESET ERROR;MASTER WALLET DATA BROKEN(部分重置落库分支) 主钱包哈希、改密、重置落库等操作失败。
TO910 ERRCODE_UDBTO_CRYPTO_MASTER WALLET HASH ERROR;CREATE MASTER WALLET ERROR;MASTER WALLET CHANGE ERROR;INVALID RESET PARAMS 主钱包/全局 SK 加解密、派生与重置参数错误。
TO911 ERRCODE_UDBTO_CRYPTO_BUCKET BUCKET FILE KEY DERIVE;BUCKET KEY CREATE;BUCKET RECOVERY DECRYPT ERROR 等 桶钱包密钥、mode 密文、恢复解密失败。
TO912 ERRCODE_UDBTO_OBJECT_PATH_EXISTS path is exist 对象路径已存在(put 冲突)。
TO913 ERRCODE_UDBTO_LIST_OBJECTS_ERROR 列举桶内对象失败相关文案 批量列举对象失败。
TO914 ERRCODE_UDBTO_INVALID_ARG INVALID WALLET INPUT;MASTER WALLET NAME NOT MATCH;INVALID RESET TARGET TYPE 钱包/重置/桶名等入参非法。
TO915 ERRCODE_UDBTO_METADATA_NOT_FOUND BUCKET RESET INFO NOT FOUND;BUCKET RECOVERY DATA NOT FOUND 重置/恢复所需元数据行不存在。
TO916 ERRCODE_UDBTO_METADATA_CORRUPT MASTER WALLET DATA BROKEN;BUCKET RESET INFO BROKEN 钱包或重置包内容损坏。
TO917 ERRCODE_UDBTO_AUTH_FAILED MASTER WALLET PASSWORD ERROR;MASTER WALLET OLD PASSWORD ERROR;BUCKET VERIFY KEY ERROR;BUCKET PASSWORD ERROR;MASTER WALLET RESET CODE ERROR 等 口令/校验码/重置码错误(含已 OPEN 时重复 open 传错密码)。
TO918 ERRCODE_UDBTO_DELETE_OBJECTS_ERROR UDBTO DELETE BUCKET OBJECTS ERROR 批量删除桶内对象失败。
TO919 ERRCODE_UDBTO_OBJECT_META_ERROR (预留) 对象元数据表更新失败等;见后续版本与 error.md。