我尝试从SQL文件导入数据库转储,但在将字符串Mér插入定义为variang(3)的字段时,插入失败。我没有捕捉到确切的错误,但它指向特定的值,并带有variable(3)的约束
考虑到我认为这对我当时正在做的事情并不重要,我只是将值更改为Mer,它起了作用,我继续前进
字段的限制是否考虑到字节字符串的长度而变化?真正让我吃惊的是,这是从另一个PostgreSQL数据库中转储的。因此,约束如何允许最初写入值是没有意义的
由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\U ASCII数据库中
例如,我在UTF-8数据库中执行了以下操作:
创建模式so4249745;
创建表so4249745.t(键varchar(3)主键);
插入so4249745.t值(“Mér”);
然后将其转储并尝试将其加载到SQL_ASCII数据库:
pg_dump-f dump.sql--schema=so4249745--table=t
createdb-E SQL_ASCII-T模板0测试
psql-f dump.sql enctest
果然:
psql:dump.sql:34:错误:值太长,无法更改类型字符(3)
上下文:复制t,第1行,列键:“Mér”
相比之下,如果我将数据库enctest创建为编码为LATIN1或UTF8,那么它的加载很好
出现此问题的原因是将数据库转储为多字节字符编码,并尝试将其恢复为SQL_ASCII数据库。使用SQL_ASCII基本上禁用了客户机数据到服务器数据的转码,并假设每个字符有一个字节,让客户机负责使用正确的字符映射。由于转储文件包含存储为UTF-8的字符串,即四个字节,因此SQL_ASCII数据库将其视为四个字符,因此认为它违反了约束。它打印出值,然后我的终端将其重新组合为三个字符