alibaba / canal

阿里巴巴 MySQL binlog 增量订阅&消费组件
Apache License 2.0
28.54k stars 7.62k forks source link

LogBuffer 解析 Decimal 数据未补齐末尾0的精度问题 #5313

Open F-ca7 opened 3 weeks ago

F-ca7 commented 3 weeks ago

复现方式:

// 字段类型 decimal(64, 16)
public static void main(String[] args) {
    byte[] bytes = new byte[] {-128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    LogBuffer logBuffer = new LogBuffer(bytes, 0, bytes.length);
    BigDecimal decimal = logBuffer.getDecimal(64, 16);
    System.out.println(decimal.toPlainString());
}

预期输出:"0.0000000000000000" (16个0)

实际输出:"0.000000000" (9个0)

错误原因:解析最后一段小数的时候,没有补齐0至原始数据类型定义的小数精度

https://github.com/alibaba/canal/blob/66483ef83fd9736ea587353674dfbd29d447b11e/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java#L1546

修复方案:补齐指定scale后的0

for (int i = pos - mark; i < frac; i++) {
    buf[pos++] = ('0');
}

MySQL 源码中最后也有补齐0的操作:

// Append padding if a fixed precision was specified.
for (int i = 0; i < fill; ++i) *to++ = '0';