【openGauss】把应用开发中的设置客户端字符编码往细了说

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

早前写过两篇有关Oracle字符集的文章
【ORACLE】谈一谈Oracle数据库使用的字符集,不仅仅是乱码
【ORACLE】谈一谈NVARCHAR2、NCHAR、NCLOB等数据类型和国家字符集
基本说明了 ”数据字符编码“、”客户端字符编码“、”数据库字符编码“三者的关系这些关系对于openGauss/MogDB/postgresql其实是一样的即”数据字符编码“和”客户端字符编码“应保持一致且对应的字符集为”数据库字符集“的”子集“。但是实际应用开发中”客户端字符编码“在没有进行主动设定时往往会受各种因素干扰比如各种环境变量和数据库参数。本文就针对各种客户端通过文档加实验来对客户端字符编码应该如何设置来做个分析说明注本文的测试环境操作系统为centos7.9

设置客户端字符编码的各种方式

在openGauss中可以通过很多方式来设置客户端字符集但实际上最终影响的都是在数据库连接中的 client_encoding该值可以通过执行 show client_encoding; 来进行查看。

操作系统环境变量

  1. LANG
export LANG=zh_CN.UTF-8
  1. LC_CTYPE
export LC_CTYPE=zh_CN.UTF-8
  1. PGCLIENTENCODING
export PGCLIENTENCODING=UTF-8

数据库级参数

  1. alter databse dbname set client_encoding=‘UTF-8’;
    (注意alter system set client_encoding=‘UTF-8’; 无法执行)

数据库会话中的参数(会话级)

  1. set client_encoding=‘UTF-8’;
  2. set client_encoding to ‘UTF-8’;
  3. set names ‘UTF-8’;
  4. alter session set client_encoding = ‘UTF8’;

gsql元命令(会话级)

  1. \encoding UTF-8

驱动连接参数

  1. libpq
conninfo = "dbname=postgres port=26100 host='192.168.56.117' client_encoding='UTF-8' application_name=test connect_timeout=5 sslmode=disable user='xxx' password='xxx' ";
  1. jdbc
urlString = "jdbc:opengauss://192.168.56.117:26100/postgres?batchMode=off&allowEncodingChanges=true&characterEncoding=GBK";
  1. psycopg2
conn = psycopg2.connect(database="postgres", user="xxx", password="xxx", host="192.168.56.117", port="26100",client_encoding="GBK")

postgresql.conf

client_encoding= 'UTF8'

(注由于设置postgresql.conf的方式在我测试的各种情况下均未产生影响为节省篇幅下文不再列出这种方式)

各客户端中取值的优先级和默认值

由于可以设置的方式太多,那么必然会出现各个设置互相冲突时以哪个为准的问题。
首先明确一点当前会话执行的客户端字符编码一定是可以通过
show client_encoding; 这个命令查看的也就是说无论怎么设置最终都是为了影响会话里的 client_encoding 参数。接着我们可以进行各种组合尝试来确认这个参数的默认值及获取来源
image

根据以上测试结果可以得到如下几个结论
对于gsql

  1. 不论数据库字符编码为何值其client_encoding会从PGCLIENTENCODING获取
  2. 当PGCLIENTENCODING没有设置时会从环境变量LC_CTYPE获取
  3. 当LC_CTYPE没有设置时会从环境变量LC_ALL获取
  4. 当LC_ALL没有设置时会从环境变量LANG获取
  5. 当LANG没有设置时unset LANG默认为sql_ascii
no
no
no
no
no
yes
yes
yes
yes
gsql
client_encoding 是否有在会话中设置
PGCLIENTENCODING 是否有设置
LC_CTYPE 是否有设置
LC_ALL是否有设置
LANG是否有设置
client_encoding 为 SQL_ASCII
client_encoding 取LC_ALL 的值
client_encoding 取LC_CTYPE 的值
client_encoding 取PGCLIENTENCODING 的值
client_encoding 为设定的值

对于libpq

  1. 不论LANG为何值其client_encoding会从PGCLIENTENCODING获取
  2. 当PGCLIENTENCODING没有设置时会从database的client_encoding获取(select * from pg_catalog.pg_db_role_setting )
  3. 当database的client_encoding没有设置时默认为数据库建库时的字符编码select getdatabaseencoding();
no
no
no
yes
yes
yes
libpq
client_encoding 是否有在会话中设置
PGCLIENTENCODING 是否有设置
pg_catalog.pg_db_role_setting 是否有设置client_encoding
getdatabaseencoding
pg_catalog.pg_db_role_setting 的client_encoding
client_encoding 取PGCLIENTENCODING 的值
client_encoding 为设定的值

简单来说就是libpq不认LANG、LC_CTYPEgsql不认alter database和数据库字符编码。在不修改应用程序代码时想指定客户端字符编码最佳方式为设置PGCLIENTENCODING因为这样才能保证两者的表现一致。

以上同样的测试我用psycopg2jdbc也测了一遍和libpq的表现是完全一致的也就是说设定PGCLIENTENCODING对各种程序开发是最通用的设定客户端字符集编码的方案。单一程序处理多种字符编码的情况本文暂不考虑

其他

附上我用libpq测试之前文章中的用例结果可以发现能插入的结果和ORACLE表现是完全一致的有区别的是ORACLE中字符编码错了可能也能插入而MogDB/openGauss/postgresql中则会报错这个点是好是坏也只能见仁见智区分场景了。但我个人偏向于应尽量避免让错误的数据进入数据库
image-1675252053480

本文结论为参考了各种官方文档加上自己的测试得到的结果如有不对请联系我指出。

参考链接

https://docs.mogdb.io/zh/mogdb/v3.0/character-set-support
https://docs.mogdb.io/zh/mogdb/v3.0/1-gsql
http://postgres.cn/docs/13/libpq-connect.html

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6