有人在DuckDB微信群里提到一个奇怪的bug, 模糊查询绿字开头的行就会报错,我验证了一下确实存在
D create table t as select '绿色' a;
D from t where a like '绿';
┌─────────┐
│ a │
│ varchar │
├─────────┤
│ 0 rows │
└─────────┘
D from t where a like '绿%';
Invalid Input Error:
Invalid unicode (byte sequence mismatch) detected in value construction
D from t where a like '绿_%';
Invalid Input Error:
Invalid unicode (byte sequence mismatch) detected in value construction
而如果把绿字改成其他的字比如黄,就没有问题。
张泽鹏先生发现,这个问题与绿字的UTF8编码有关,他跟踪到代码发现DuckDB还要查绿字的UTF8编码E7BBBF+1的代码,而那个E7BBC0码不是有效的UTF8编码,同样情况的例子是弿字。
为什么查绿字,还要查编码比它大1的字,看like执行计划就知道了。
D create or replace table temp as select i::varchar n from range(10000000)t(i);
D explain analyze select sum(length(n)) from temp where n like '5%';
┌─────────────┴─────────────┐
│ TABLE_SCAN │
│ ──────────────────── │
│ Table: temp │
│ Type: Sequential Scan │
│ Projections: n │
│ │
│ Filters: │
│ n>='5' AND n<'6' │
│ │
│ 11,111,111 rows │
│ (0.24s) │
└───────────────────────────┘
原来,优化器把like '5%'改写成了n>='5' AND n<'6', 后面多出来的’6’就是’5’的编码+1的结果。
知道了原理,规避方法也很简单
找一个UTF8编码非常大的,确认大于在表中绿字后面的最大字符的字符附在我们要查的代码后面就可以了。
在网上查到“在 UTF-8 编码中,中文字符的范围主要集中在 Unicode 的 \u4E00-\u9FA5 区间。这一范围涵盖了大部分常用汉字。此外,还有更广泛的范围 \u2E80-\u9FFF,包括了日韩汉字等扩展字符。”
那在绿字后面放一个unicode为9FFF的字符就好了。
from t where a >='绿_%' and a<='绿'||chr('0x9FFF'::int);
┌─────────┐
│ a │
│ varchar │
├─────────┤
│ 绿色 │
└─────────┘


被折叠的 条评论
为什么被折叠?



