pg编码相关问题梳理

本文梳理了Lightdb/PG编码相关问题,记录了导入数据时的报错情况。涉及终端工具、服务器、客户端和服务端的编码设置,分ltsql/psql交互式执行SQL和通过-f选项执行SQL文件两种情况讨论,还说明了不同编码设置不一致时的报错及解决办法,最后有gdb跟踪附录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Lightdb/PG 编码相关问题梳理

之前在通过SQL文件导入数据时,报:ERROR: invalid byte sequence for encoding "EUC_CN"错误。然后就梳理了一下编码相关问题,这边记录一下。涉及到如下两种类型的报错:

  • ERROR: invalid byte sequence for encoding “EUC_CN”
  • ERROR: character with byte sequence 0xad 0xe5 in encoding “GBK” has no equivalent in encoding “UTF8”

在使用Lightdb 过程中会涉及到多处的编码设置。具体如下:

  • 终端工具(如:xshell)的编码,一般与服务器设置一致
  • 服务器编码(locale, LANG)
  • LightDB 客户端编码 client_encoding
  • LightDB 服务端编码 server_encoding

下面分两种情况进行讨论,一种是通过ltsql/psql 交互式执行SQL, 一种是通过-f 选项执行SQL文件

一. 前期准备

create database test_c template template0 encoding 'EUC_CN' LC_CTYPE 'zh_CN' LC_COLLATE 'zh_CN';
\c test_c

二. ltsql/psql 设置client_encoding 原理

ltsql/psql 设置client_encoding 主要考虑以下几个方面:

  • PGCLIENTENCODING 环境变量, 本文不考虑, 认为没有设置(设置了,即设置为PGCLIENTENCODING值)
  • 是否不是终端,通过pset.notty= (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); 判断,标记,只有当输入输出都不是终端时,才会标记为非终端模式。

交互式执行SQL命令, 必然是终端, 然后会设置client_encoding 为 auto((pset.notty || lt_getenv("PGCLIENTENCODING")) ? NULL : "auto";) , 最终在建立连接时, 对于auto会从locale获取编码设置(conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))), 即根据服务器编码来设置client_encoding。

非交互式的执行SQL脚本,则会更复杂一点, 在下面进行说明。

如果客户端不设置client_encoding (非终端模式), 则服务端会把client_encoding 设置为server_encoding.

三. SQL命令

  • 当client_encoding与终端工具编码不一致时, 会报 ERROR: invalid byte sequence for encoding "EUC_CN",

    • 终端编码为unicode(UTF8), 服务端编码也为UTF8, client_encoding 初始值为UTF8, 下面通过set 命令修改客户端编码(也可以通过修改服务端编码,然后重连)

      chuhx@postgres=# show client_en%;
            name       | setting |                description                
      -----------------+---------+-------------------------------------------
       client_encoding | UTF8    | Sets the client's character set encoding.
      (1 row)
      
      
      chuhx@postgres=# set client_encoding= EUC_CN;  # gbk2312
      SET
      chuhx@postgres=# show client_en%;
            name       | setting |                description                
      -----------------+---------+-------------------------------------------
       client_encoding | EUC_CN  | Sets the client's character set encoding.
      (1 row)
      chuhx@postgres=# show server_en%;
            name       | setting |                    description                    
       
      -----------------+---------+---------------------------------------------------
      -
       server_encoding | UTF8    | Sets the server (database) character set encoding.
      (1 row)
      chuhx@postgres=# insert into test values('是打算');
      ERROR:  invalid byte sequence for encoding "EUC_CN": 0xe6 0x98
      
      这个报错是由于输入文本为UTF8(终端编码), 但client_encoding 为EUC_CN(gbk2312), 导致不能用EUC_CN编码正确解析输入的文本导致。
      
    • 修改终端编码 gbk2312, 修改后就不会报错。
      ssh encoding

      chuhx@postgres=# insert into test values('是打算');
      INSERT 0 1
      
  • 当client_encoding与终端工具编码不一致时,也可能会报ERROR: character with byte sequence 0xad 0xe5 in encoding "GBK" has no equivalent in encoding "UTF8"

    • 比如client_encoding 为gbk, server_encoding 为utf8, 终端编码为utf8, 此时会有如下报错:

      chuhx@postgres=# insert into test values('中国');
      ERROR:  character with byte sequence 0xad 0xe5 in encoding "GBK" has no equivalent in encoding "UTF8"
      
    • 修改终端编码为 gbk 即可成功插入, 这是因为原先插入的是’中国‘(utf8编码)按gbk 进行了解析。但没有解析出错(没有检测到)。在转换为utf8 时报错。

      chuhx@postgres=# insert into test values('中国');
      INSERT 0 1
      
  • client_encoding 与server_encoding 不一致,可能出现如下报错:character with byte sequence 0xe7 0x99 0xbc in encoding "UTF8" has no equivalent in encoding "EUC_CN", 这是由于’發‘ 不能用EUC_CN 编码表示,需要修改lightdb 服务端编码。

    chuhx@test_c=# insert into test values('發');
    ERROR:  character with byte sequence 0xe7 0x99 0xbc in encoding "UTF8" has no equivalent in encoding "EUC_CN"
    chuhx@test_c=# 
    

四. SQL文件

在通过 -f/-c 非交互方式执行SQL时, 满足了!isatty(fileno(stdin)), 如果对结果重定向到文件,则满足了 !isatty(fileno(stdout))); 此时,表示ltsql执行在非终端模式下,不会设置client_encoding, 当客户端不设置client_encoding时, 服务端会把client_encoding 设置为与server_encoding 一致。具体见如下:

[chuhx@test-host ~/citus]$ ltsql -p5432 -d test_c -c 'show %encoding;'   
      name       | setting |                    description                    
 
-----------------+---------+---------------------------------------------------
-
 client_encoding | UTF8    | Sets the client's character set encoding.
 server_encoding | EUC_CN  | Sets the server (database) character set encoding.
(2 rows)

重定向后, 非终端模式, client_encoding 与server_encoding 一致
[chuhx@test-host ~/citus]$ ltsql -p5432 -d test_c -c 'show %encoding;' > 1.txt
[chuhx@test-host ~/citus]$ cat 1.txt 
      name       | setting |                    description                     
-----------------+---------+----------------------------------------------------
 client_encoding | EUC_CN  | Sets the client's character set encoding.
 server_encoding | EUC_CN  | Sets the server (database) character set encoding.
(2 rows)

[chuhx@test-host ~/citus]$ 

用-o 指定输出文件, 会根据服务端编码设置client_encoding。
[chuhx@test-host ~/citus]$ ltsql -p5432 -d test_c -c 'show %encoding;' -o 1.txt 
[chuhx@test-host ~/citus]$ cat 1.txt 
      name       | setting |                    description                     
-----------------+---------+----------------------------------------------------
 client_encoding | UTF8    | Sets the client's character set encoding.
 server_encoding | EUC_CN  | Sets the server (database) character set encoding.
(2 rows)

五. 附录(gdb 跟踪)

r -p5432 -d test_c -f test.sql -o 1.txt 只有输入不是终端
pset.notty false

Breakpoint 3, PQconnectdbParams (keywords=0x6bc180, values=0x6bc1d0, expand_dbname=1) at fe-connect.c:651
651		PGconn	   *conn = PQconnectStartParams(keywords, values, expand_dbname);
(gdb) p keywords[6]
$60 = 0x48f9ed "client_encoding"
(gdb) p values[6]
$61 = 0x48fa0e "auto"
(gdb) c
Continuing.

Breakpoint 5, connectOptions2 (conn=0x6bc220) at fe-connect.c:1091
1091		conn->whichhost = 0;
(gdb) p conn->client_encoding
$62 = 0
(gdb) p conn->client_encoding_initial
$63 = 0x6c6470 "auto"
(gdb) c
Continuing.

Breakpoint 6, connectOptions2 (conn=0x6bc220) at fe-connect.c:1457
1457		if (conn->client_encoding_initial &&
(gdb) n
1458			strcmp(conn->client_encoding_initial, "auto") == 0)
(gdb) 
1457		if (conn->client_encoding_initial &&
(gdb) 
1460			free(conn->client_encoding_initial);
(gdb) 
1461			conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
(gdb) 
1462			if (!conn->client_encoding_initial)
(gdb) 
1469		if (conn->target_session_attrs)
(gdb) p conn->client_encoding_initial
$64 = 0x6c6470 "UTF8"

r -p5432 -d test_c -f test.sql > 1.txt 输入输出都不是终端
pset.notty true

Breakpoint 3, PQconnectdbParams (keywords=0x6bbf40, values=0x6bbf90, expand_dbname=1) at fe-connect.c:651
651		PGconn	   *conn = PQconnectStartParams(keywords, values, expand_dbname);
(gdb) p keywords[6]
$66 = 0x48f9ed "client_encoding"
(gdb) p values[6]
$67 = 0x0
(gdb) c
Continuing.

Breakpoint 5, connectOptions2 (conn=0x6bbfe0) at fe-connect.c:1091
1091		conn->whichhost = 0;
(gdb) p conn->client_encoding
$68 = 0
(gdb) p conn->client_encoding_initial
$69 = 0x0
(gdb) 
<think>好的,用户想查询PostgreSQL数据库的字符编码设置,具体是怎么操作呢。首先我需要回忆一下PostgreSQL相关命令。之前学过的,查看字符编码通常有两种方式:查看客户端编码和服务端编码。 根据引用里的资料,特别是引用2和引用3,里面提到PostgreSQL可以通过SQL命令来查询。比如用`SHOW client_encoding;`和`SHOW server_encoding;`这两个命令。用户可能想同时知道客户端和服务端的设置,所以应该把这两个命令都列出来。 另外,用户可能还需要查看特定数据库编码设置。这时候可能需要查询系统目录,比如`pg_database`表里的`encoding`字段。例如,执行`SELECT datname, pg_encoding_to_char(encoding) FROM pg_database;`就能列出所有数据库及其字符编码。这一步在引用3里也有提到,PostgreSQL支持的字符集类型,比如UTF-8、EUC_CN等。 还需要注意不同操作系统的限制,比如Windows不支持EUC_CN作为服务端编码,所以在Windows上服务端编码通常是UTF-8。用户可能在创建数据库时需要注意这一点,但当前问题只是查看,所以暂时不需要深入讨论。 另外,用户可能会混淆客户端和服务端编码的区别,需要解释清楚。服务端编码数据库存储数据时使用的编码,而客户端编码是连接时的编码设置,两者一致才能避免乱码问题。 最后,确保提供的命令示例正确,并且建议用户使用psql命令行工具执行这些查询,或者通过图形化工具如pgAdmin来查看。可能还需要提醒用户权限问题,是否有权限访问系统表。</think>在PostgreSQL中查看字符编码设置可通过以下两种主要方式实现: ### 一、查看服务端与客户端编码 1. **服务端编码**(数据库存储编码) ```sql SHOW server_encoding; ``` 将返回类似结果:`UTF8`(最常见)、`EUC_CN`等[^3] 2. **客户端编码**(当前会话编码) ```sql SHOW client_encoding; ``` 通常与服务端编码保持一致以避免乱码 ### 二、查看具体数据库编码 通过系统目录查询所有数据库编码: ```sql SELECT datname, pg_encoding_to_char(encoding) FROM pg_database; ``` 示例输出: ``` datname | pg_encoding_to_char ----------+---------------------- postgres | UTF8 testdb | EUC_CN ``` ### 三、编码兼容性说明 - Windows系统服务端**仅支持UTF8**编码[^3] - 简体中文支持包含:`UTF8`、`EUC_CN`、`GBK`(仅客户端)、`GB18030`(仅客户端) - 建议优先使用`UTF8`保证多语言兼容性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

紫无之紫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值