我尝试从SQL文件导入数据库转储,并且在将字符串Mér
插入定义为varying(3)
的字段时插入失败。我没有捕获确切的错误,但它指出了具有varying(3)
约束的特定值。
鉴于我认为这对我当时正在做的事情不重要,我只是将值更改为Mer
,它有效,我继续前进。
varying
字段的限制是否考虑了字节字符串的长度?令我难以置信的是,这是从另一个PostgreSQL数据库转储的。因此,约束如何允许最初写入值是没有意义的。
答案 0 :(得分:32)
varchar(N)
类型强加的长度限制由length
函数计算,以字符为单位,而不是字节。因此,'abcdef'::char(3)
被截断为'abc'
,但'a€cdef'::char(3)
被截断为'a€c'
,即使在编码为UTF-8的数据库的上下文中,'a€c'
也会被编码使用5个字节。
如果恢复转储文件,则抱怨'Mér'
不会进入varchar(3)
列,这表示您正在将UTF-8编码的转储文件还原到SQL_ASCII数据库中。
例如,我在UTF-8数据库中执行了此操作:
create schema so4249745;
create table so4249745.t(key varchar(3) primary key);
insert into so4249745.t values('Mér');
然后将其转储并尝试将其加载到SQL_ASCII数据库中:
pg_dump -f dump.sql --schema=so4249745 --table=t
createdb -E SQL_ASCII -T template0 enctest
psql -f dump.sql enctest
果然:
psql:dump.sql:34: ERROR: value too long for type character varying(3)
CONTEXT: COPY t, line 1, column key: "Mér"
相比之下,如果我创建数据库enctest作为编码LATIN1或UTF8,它加载正常。
由于将数据库转储为多字节字符编码并尝试将其还原到SQL_ASCII数据库中,因此会出现此问题。使用SQL_ASCII基本上禁止将客户端数据转码为服务器数据,并假定每个字符占一个字节,让客户端负责使用正确的字符映射。由于转储文件包含存储的字符串为UTF-8,即四个字节,因此SQL_ASCII数据库将其视为四个字符,因此将其视为违反约束。它打印出值,然后我的终端重组为三个字符。
答案 1 :(得分:4)
它取决于您在创建数据库时使用的值。 createdb -E UNICODE
创建一个Unicode DB,它也应该接受多字节字符并将它们计为一个字符。
您可以使用
psql -l
查看使用了哪种编码。 This page有一个表,其中包含有关每个字符使用的字节数的信息。