[LightDB兼容增强]支持'\0'插入char(1)
支持的版本:自LightDB 23.3。
背景:
在Oracle以及Mysql中,'\0'可以正常插入char(n)类型字符串中,然而PG内核对字符类型的处理均以'\0'作为结尾符处理,并且所有的输入字符都会在服务器端进行编码校验,当发现字符串中有'\0'字符的时候,直接认为字符串无效,并报错结束。
因此'\0'在PG中并不是一个可以用来作为业务表达的字符值,比如chr(0), E'\0'均作错误处理,最终导致jdbc/libpq绑定变量也无法插入'\0'到char(n)中。
解决方案:
为解决此兼容问题,满足特定场景的需求,并尽最大可能性减少对原有的系统改造,LightDB针对JDBC以及Libpq绑定变量中'\0'插入char(n)类型字段作了特别的处理。
LightDB引入了一个新的GUC参数,lightdb_ascii_zero_store_value, 此参数在创建数据库时指定,之后不能修改。如果此值非零,并且数据库模式为Oracle或者Mysql, 当插入字符'\0'的时候,我们会对‘\0’进行替换操作,如果插入'\0abc'或'abc\0'行为模式不受影响,为维持'\0'值在系统的行为一致,E'\0', CHR(0)的相应逻辑也作了更新。
练习:
1,创建数据库
create database oradb with lightdb_ascii_zero_store_value 20 lightdb_syntax_compatible_type 'oracle'; \c oradb
2,创建表,并插入数据
lightdb@oradb=# create table t00(a char(1), b char(2)); CREATE TABLE lightdb@oradb=# insert into t00(a,b) values(E'\0',E'\0'); INSERT 0 1 lightdb@oradb=# insert into t00(a,b) values(chr(0),chr(0)); INSERT 0 1 lightdb@oradb=# lightdb@oradb=# select * from t00; a | b ------+------- \x14 | \x14 \x14 | \x14 (2 rows)
3,条件查询,更新,删除
lightdb@oradb=# select * from t00 where a=E'\0'; a | b ------+------- \x14 | \x14 \x14 | \x14 (2 rows) lightdb@oradb=# UPDATE t00 SET b='a' where a=E'\0' LIMIT 1; UPDATE 1 lightdb@oradb=# select * from t00; a | b ------+------- \x14 | \x14 \x14 | a (2 rows) lightdb@oradb=# delete from t00 where a=E'\0'; DELETE 2 lightdb@oradb=# select * from t00; a | b ---+--- (0 rows)
4,若字符串长度超过2,或者模式不为Oracle,也不为Mysql, 或lightdb_ascii_zero_store_value为0,行为模式均维持与原生PG(13.8)一致。
lightdb@oradb=# insert into t00(a,b) values(E'\0a',E'a\0'); ERROR: invalid byte sequence for encoding "UTF8": 0x00 lightdb@oradb=# create database dboff with lightdb_syntax_compatible_type 'off'; CREATE DATABASE lightdb@oradb=# \c dboff; You are now connected to database "dboff" as user "lightdb". compatible type: postgresql lightdb@dboff=# select E'\0'; ERROR: invalid byte sequence for encoding "UTF8": 0x00 lightdb@dboff=# select chr(0); ERROR: null character not permitted lightdb@dboff=# create database oradb2 with lightdb_syntax_compatible_type 'oracle' lightdb_ascii_zero_store_value 0; NOTICE: auto create user "oradb2" success CREATE DATABASE lightdb@dboff=# \c oradb2 You are now connected to database "oradb2" as user "lightdb". compatible type: oracle lightdb@oradb2=# select E'\0'; ERROR: invalid byte sequence for encoding "UTF8": 0x00 lightdb@oradb2=# select CHR(0); ERROR: null character not permitted
5,JDBC, Libpq使用
JDBC,Libpq在插入'\0'的时候,我们仅拦截并修改了文本类型的'\0'参数,如果绑定变量为'\0a'或'a\0'或为非文本类型,并不受此影响。
在使用JDBC的绑定参数的时候,请确认传递的类型为文本类型(使用SetString)
在使用Libpq的时候,请确定使用与LightDB匹配的Libpq版本,并且设置为文本类型相关参数,因为Libpq默认会因'\0'结尾的原因导致字符并不会发送到服务端,因此LightDB对应匹配的Libpq版本针对此情况专门做了修改。
6,其它注意事项
1,lightdb_ascii_zero_store_value取值范围为[0,32], 默认值为0(为0时,行为不受影响),非oracle, mysql模式,行为不受影响。
2,因为'\0'的值被替换,其它的行为也会受到影响,最终跟被替换的值保持一致。例如大小比较,取长度,拼接等接受字符为输入参数的函数。
3,存储在表中的值为最终替换值,读行时,读到的值也是此存储值。业务中如果有需要做二次映射的,需要业务自己处理。
4,此替换行为,目前只能在oracle, mysql模式下生效,创建业务数据库的时候,需要选择正确的模式。