对象模态
九有数据库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. 业务表:只存「对象路径」关联 ==========
CREATE TABLE articles (
article_name text,
article_file text -- 存 s3://... 路径,不存 bytea 正文
);
依次创建并打开钱包 和 钱包桶(lz4|sm4压缩加密模式), 命令同上
插入数据
-- ========== 写入: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/省略
)
);
查询数据
-- ========== 读取:用表中路径 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 钱包与钱包桶如何协同(用户需要记住的规则)
钱包:打开钱包,表示本会话已被授权处理「受钱包约束」的钱包桶类操作;关闭或断线后,这一授权不延续到别的连接。
钱包桶:每个钱包桶可以有自己的密码与策略;即使钱包已打开,对某个加密/压缩钱包桶读写前,通常仍需再打开钱包桶。这样即使多人共用同一数据库账号,不知道钱包桶密码仍无法操作受保护的钱包桶内的对象。
对象仍在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。 |