首批通过分布式安全可靠测评,为关键业务系统打造
数据的 collation type 不一致导致合并报 -4016
更新时间:2026-06-15 09:16
问题现象
存储层合并报错 -4016,报错日志如下。
2023-08-07 10:30:34.150054] WARN [COMMON] compare (ob_obj_compare.cpp:3500) [2310751][0][YB420ADC8116-000600FCCB4CC7D4-0-0] [lt=12] [dc=0] obj1 and obj2 can't compare(obj1={"VARCHAR":"9a58fd6331ac11eeba95f46b8c9686ed", collation:"utf8mb4_bin", coercibility:"IMPLICIT"}, obj2={"VARCHAR":"9a58fd6331ac11eeba95f46b8c9686ed", collation:"utf8mb4_general_ci", coercibility:"IMPLICIT"}, obj1.get_meta()={type:"VARCHAR", collation:"utf8mb4_bin", coercibility:"IMPLICIT"}, obj2.get_meta()={type:"VARCHAR", collation:"utf8mb4_general_ci", coercibility:"IMPLICIT"})
问题原因
PL 中 subquery 结果 varchar 类型的 collation 与期待值不一致时未做类型转换。
结果值作为表某一列的值插入到存储层,如果这一列是表的主键或索引列时,存储层在做数据合并时会报错 -4016。
问题的风险及影响
数据合并报错 -4016。
影响版本
OceanBase 数据库 V3.2.3 BP9(oceanbase-3.2.3.3-109000182023071410)、V3.2.4 BP5(oceanbase-3.2.4.5-105000012023081513)、V4.1.0 BP3(oceanbase-4.1.0.2-103000072023081111) 及之前的 BP 版本,V4.2.0 Beta 版本。
解决方法及规避方式
解决方法
先删除报错的索引,再进行数据合并。
下面以一个示例说明。
执行如下语句创建表 t1,包含了整型列 c1 和 varchar 列 uuid,其中 uuid 列有一个名为
idx_hrmresource_uuid的索引。obclient> create table t1 (c1 int, uuid varchar(50) COLLATE utf8mb4_bin DEFAULT '0', KEY `idx_hrmresource_uuid` (`uuid`) BLOCK_SIZE 16384 LOCAL);执行如下语句,向表 t1 中插入一条数据。
obclient> insert into t1 values (1, 'aaaa');执行如下语句创建了一个名为
trg_t1的触发器(trigger),当在表 t1 中执行插入操作之前触发。DELIMITER // create trigger trg_t1 before insert on t1 for each row begin declare a int; set new.uuid = (select replace(uuid(), '-', '')); end; // DELIMITER ;插入一条数据发现 4016 报错。
obclient> insert into t1 values (1, null);执行例子会直接报错 -4016,因为
subquery表达式 (select replace(uuid()),'-','') 计算结果collation是utf8mb4_general_ci,new.uuid的collation是utf8mb4_bin,二者的collation不一致会导致存储层报错。执行如下语句
DROP掉索引后再INSERT数据就不会报错了。obclient> alter table t1 drop key idx_hrmresource_uuid;升级到问题已修复版本。目前已修复的版本包含 V3.2.3 BP10(oceanbase-3.2.3.3-110000092023091219)、V3.2.4 BP6(oceanbase-3.2.4.6-106000062023110109)、V4.1.0 BP4(oceanbase-4.1.0.2-104000032023092119)、V4.2.0 GA(oceanbase-4.2.0.0-100010082023083014)及之后版本。
规避方式
在 PL subquery 表达式外面包一层 cast 函数,cast 到期待的 collation。
如上面的例子修改 trigger 定义如下。
create trigger trg_t1 before insert on t1 for each row
begin
declare a int;
set new.uuid = (select cast(replace(uuid(), '-', '') as char CHARACTER SET utf8mb4) COLLATE utf8mb4_bin);
end;
/