PostgreSQL服务器管理:本地化

    xiaoxiao2021-04-18  152

    本文档为PostgreSQL 9.6.0文档,本转载已得到原译者彭煜玮授权。

    1. 区域支持

    区域支持指的是应用遵守文化偏好的问题,包括字母表、排序、数字格式等。PostgreSQL使用服务器操作系统提供的标准 ISO C 和POSIX的区域机制。更多的信息请参考你的系统的文档。

    1.1. 概述

    区域支持是在使用initdb创建一个数据库集簇时自动被初始化的。默认情况下,initdb将会按照它的执行环境的区域设置初始化数据库集簇; 因此如果你的系统已经设置为你的数据库集簇想要使用的区域, 那么你就没有什么可干的。如果你想使用其它的区域(或者你还不知道你的系统设置的区域是什么),那么你可以用--locale选项准确地告诉initdb你要用哪一个区域。 比如:

    initdb --locale=sv_SE这个Unix系统上的例子把区域设置为瑞典(SE)瑞典语(sv)。 其他的可能性包括 en_US(美国英语)和fr_CA(加拿大法语)。如果有多于一种字符集可以用于区域,那么声明可以采用如下的形式:language_territory.codeset。例如fr_BE.UTF-8表示在比利时(BE)讲的法语(fr),使用一个UTF-8字符集编码。

    在你的系统上有哪些区域可用取决于操作系统提供商提供了什么以及安装了什么。在大部分Unix系统上,命令locale -a将会提供一个所有可用区域的列表。Windows使用一些更繁琐的区域名,例如German_Germany或者Swedish_Sweden.1252,但是其原则是相同的。

    有时候,把几种区域规则混合起来也很有用,比如,使用英语排序规则而用西班牙语消息。 为了支持这些,我们有一套区域子类用于控制本地化规则的某些方面:

    这些类名转换成initdb的选项名来覆盖某个特定分类的区域选择。比如,要把区域设置为加拿大法语,但使用 U.S. 规则格式化货币,可以使用initdb --locale=fr_CA --lc-monetary=en_US。如果你想让系统表现得象没有区域支持,那么使用特殊的区域名C或者等效的POSIX。

    一些区域分类的值必需在数据库被创建时的就被固定。你可以为不同的数据库使用不同的设置,但是一旦一个数据库被创建,你就不能在数据库上修改这些区域分类的值。LC_COLLATE和LC_CTYPE就是这样的分类。它们影响索引的排序顺序,因此它们必需保持固定, 否则在文本列上的索引将会崩溃(但是你可以使用排序规则放松这种限制,讨论见Section 23.2)。这些分类的默认值在initdb运行时被确定,并且这些值在新数据库被创建时使用,除非在CREATE DATABASE命令中特别指定。

    其它区域分类可以在任何时候被更改,更改的方式是设置与区域分类同名的服务器配置参数。被initdb选中的值实际上只是被写入到配置文件postgresql.conf中作为服务器启动时的默认值。如果你将这些赋值从postgresql.conf中除去,那么服务器将会从其执行环境中继承该设置。

    请注意服务器的区域行为是由它看到的环境变量决定的,而不是由任何客户端的环境变量影响的。 因此,我们要在启动服务器之前认真地设置好这些变量。这样带来的一种后果是如果客户端和服务器设置成不同的区域, 那么消息可能以不同的语言呈现,实际情况取决于它们的起源地。

    Note: 在我们谈到从执行环境继承区域的时候,我们的意思是在大多数操作系统上的下列动作: 对于一个给定的区域分类,比如排序规则,按照下面的顺序评估这些环境变量, 直到找到一个被设置了的:LC_ALL、 LC_COLLATE(或者对应于相应分类的变量)、LANG。如果这些环境变量一个都没有被设置,那么将区域缺省设置为C。

    一些消息本地化库也查看环境变量LANGUAGE,它覆盖所有其它用于设置消息语言的区域设置。如果有疑问, 请参考你的操作系统的文档,特别是有关gettext的文档。

    要允许消息被翻译成用户喜欢的语言,编译时必需打开NLS(configure --enable-nls)。所有其他区域支持都会被自动编译。

    1.2. 行为

    区域设置特别影响下面的 SQL 特性:

    在文本数据上使用ORDER BY或标准比较操作符的查询中的排序顺序函数upper、lower和initcap模式匹配操作符(LIKE、SIMILAR TO和POSIX风格的正则表达式);区域影响大小写不敏感匹配和通过字符类正则表达式的字符分类to_char函数家族为LIKE子句使用索引的能力

    PostgreSQL中使用非C或非POSIX区域的缺点是性能影响。它降低了字符处理的速度并且阻止了在LIKE中对普通索引的使用。因此,只能在真正需要的时候才使用它。

    作为允许PostgreSQL在非 C 区域下为LIKE子句使用索引, 有好几种自定义操作符类可用。这些操作符类允许创建一个执行严格按字符比较的索引。另一种方法是创建使用C排序规则的索引。

    1.3. 问题

    如果根据上面解释区域支持仍然不能运转,检查一下操作系统的区域支持是否被正确配置。 要检查系统中安装了哪些区域,你可以使用命令locale -a(如果你的操作系统提供了该命令)。

    请检查PostgreSQL确实正在使用你认为它该用的区域设置。LC_COLLATE和LC_CTYPE设置都是在数据库创建时决定的,并且在除了创建数据库之外的操作中都不能被更改。其它的区域设置包括LC_MESSAGES和LC_MONETARY都是由服务器启动的环境决定的, 但是可以在运行时修改。你可以用SHOW命令检查活跃的区域设置。

    源代码目录的src/test/locale中包含PostgreSQL的区域支持的测试套件。

    那些通过分析错误消息来处理服务器端错误的客户端应用很明显会有问题,因为服务器来的消息可能会是以不同语言表示的。 我们建议这类应用的开发人员改用错误代码机制。

    维护消息翻译目录需要许多志愿者的坚持不懈的努力, 他们希望PostgreSQL以他们的语言说话。 如果以你的语言表示的消息目前还不可用或者没有完全翻译完成,那么我们很感谢你的协助。如果你想帮忙,那么请参考Chapter 53或者向开发者邮递列表发邮件。

    2. 排序规则支持

    排序规则特性允许指定每一列甚至每一个操作的数据的排序顺序和字符分类行为。这放松了数据库的LC_COLLATE和LC_CTYPE设置自创建以后就不能更改这一限制。

    2.1. 概念

    在概念上,一种可排序数据类型的每一种表达式都有一个排序规则(内建的可排序数据类型是text、varchar和char。用户定义的基础类型也可以被标记为可排序的,并且在一种可排序数据类型上的域也是可排序的)。如果该表达式是一个列引用,该表达式的排序规则就是列所定义的排序规则。如果该表达式是一个常量,排序规则就是该常量数据类型的默认排序规则。更复杂表达式的排序规则根据其输入的排序规则得来,如下所述:

    一个表达式的排序规则可以是"默认"排序规则,它表示数据库的区域设置。一个表达式的排序规则也可能是不确定的。在这种情况下,排序操作和其他需要知道排序规则的操作会失败。

    当数据库系统必须要执行一次排序或者字符分类时,它使用输入表达式的排序规则。这会在使用例如ORDER BY子句以及函数或操作符调用(如<)时发生。应用于ORDER BY子句的排序规则就是排序键的排序规则。应用于函数或操作符调用的排序规则从它们的参数得来,具体如下文所述。除比较操作符之外,在大小写字母之间转换的函数会考虑排序规则,例如lower、upper和initcap。模式匹配操作符和to_char及相关函数也会考虑排序规则。

    对于一个函数或操作符调用,其排序规则通过检查在执行指定操作时参数的排序规则来获得。如果该函数或操作符调用的结果是一种可排序的数据类型,万一有外围表达式要求函数或操作符表达式的排序规则,在解析时结果的排序规则也会被用作函数或操作符表达式的排序规则。

    一个表达式的排序规则派生可以是显式或隐式。该区别会影响多个不同的排序规则出现在同一个表达式中时如何组合它们。当使用一个COLLATE子句时,将发生显式排序规则派生。所有其他排序规则派生都是隐式的。当多个排序规则需要被组合时(例如在一个函数调用中),将使用下面的规则:

    1.如果任何一个输入表达式具有一个显式排序规则派生,则在输入表达式之间的所有显式派生的排序规则必须相同,否则将产生一个错误。如果任何一个显式派生的排序规则存在,它就是排序规则组合的结果。

    2.否则,所有输入表达式必须具有相同的隐式排序规则派生或默认排序规则。如果任何一个非默认排序规则存在,它就是排序规则组合的结果。否则,结果是默认排序规则。

    3.如果在输入表达式之间存在冲突的非默认隐式排序规则,则组合被认为是具有不确定排序规则。这并非一种错误情况,除非被调用的特定函数要求提供排序规则的知识。如果它确实这样做,运行时将发生一个错误。

    例如,考虑这个表定义:

    CREATE TABLE test1 ( a text COLLATE "de_DE", b text COLLATE "es_ES", ... );

    然后在

    SELECT a < 'foo' FROM test1;

    中,<比较被根据de_DE规则执行,因为表达式组合了一个隐式派生的排序规则和默认排序规则。但是在

    SELECT a < ('foo' COLLATE "fr_FR") FROM test1;

    中,比较被使用fr_FR规则执行,因为显式排序规则派生重载了隐式排序规则。更进一步,给定

    SELECT a < b FROM test1;

    解析器不能确定要应用哪个排序规则,因为a列和b列具有冲突的隐式排序规则。由于<操作符不需要知道到底使用哪一个排序规则,这将会导致一个错误。该错误可以通过在一个输入表达式上附加一个显式排序规则说明符来解决,因此:

    SELECT a < b COLLATE "de_DE" FROM test1;

    或者等效的

    SELECT a COLLATE "de_DE" < b FROM test1;

    在另一方面,结构相似的情况

    SELECT a || b FROM test1;

    不会导致一个错误,因为||操作符不关心排序规则:不管排序规则怎样它的结果都相同。

    如果一个函数或操作符发送一个具有可排序数据类型的结果,分配给该函数或操作符的组合输入表达式的排序规则也被考虑应用在函数或操作符的结果。因此,在

    SELECT * FROM test1 ORDER BY a || 'foo';

    中排序将根据de_DE规则完成。但这个查询:

    SELECT * FROM test1 ORDER BY a || b;

    会导致一个错误,因为即使||操作符不需要知道排序规则,但ORDER BY子句需要。按照以前,冲突可以通过使用一个显式排序规则说明符来解决:

    SELECT * FROM test1 ORDER BY a || b COLLATE "fr_FR";

    2.2. 管理排序规则

    一个排序规则是一个SQL模式对象,它将一个SQL名字映射到一个操作系统区域。特别地,它映射到一个LC_COLLATE 和LC_CTYPE的组合(正如其名字所说的,一个排序规则的主要目的是设置LC_COLLATE它控制排序顺序。但是在实际中很少有必要有一个不同于LC_COLLATE的LC_CTYPE设置,因此通过一个概念来收集这些信息比为了设置每一个表达式的LC_CTYPE而创建另一种架构要更加方便)。此外,一个排序规则是和一个字符集编码绑定在一起的。相同的排序规则名字可能存在于不同的编码中。

    在所有的平台上,名为default、C和POSIX的排序规则都可用。附加的排序规则是否可用取决于操作系统的支持。default排序规则选择在数据库创建时指定的LC_COLLATE和LC_CTYPE值。C和POSIX排序规则都指定了"传统的C"行为,在其中只有ASCII字母"A"到"Z"被视为字母,并且排序严格地按照字符编码的字节值完成。

    如果操作系统支持在一个程序中使用多个区域(newlocale和相关函数),那么在一个数据集簇被初始化时,initdb将以它在操作系统上能找到的所有区域为基础在系统目录pg_collation中填充排序规则。例如,操作系统可能会提供一个名为de_DE.utf8的区域。initdb则会创建一个用于编码UTF8的名为de_DE.utf8的排序规则,在其中LC_COLLATE和LC_CTYPE都被设置为de_DE.utf8。它也会创建一个具有去掉名称的.utf8标签的排序规则。这样你也可以使用名字de_DE来使用该排序规则,这写起来更简单并且使得名字更加独立于编码。不过要注意,最初的排序规则名称的集合是平台依赖的。

    万一所需要的一个排序规则具有和LC_COLLATE及LC_CTYPE不同的值,可以使用CREATE COLLATION命令创建一个新的排序规则。该命令也可以被用于从一个现有的排序规则创建一个新的排序规则,这样对于可以在应用中使用操作系统独立的排序规则名很有用。

    在任何特定的数据库中,只有使用数据库编码的排序规则是令人感兴趣的。其他pg_collation中的项会被忽略。因此,一个如de_DE的被剥离的排序规则名在一个给定数据库中可以被认为是唯一的,即使它在全局上并不唯一。我们推荐使用被剥离的排序规则名,因为在你决定要更改到另一个数据库编码时需要做的事情更少。但是要注意default、C和POSIX排序规则在使用时可以不考虑数据库编码。

    PostgreSQL在碰到具有相同属性的不同排序规则对象时会认为它们是不兼容的。因此对于例子:

    SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1;

    将会得到一个错误,即使C和POSIX排序规则具有相同的行为。因此,我们不推荐混合使用被剥离的和非被剥离的排序规则名。

    3. 字符集支持

    PostgreSQL里面的字符集支持你能够以各种字符集存储文本,包括单字节字符集,比如 ISO 8859 系列,以及多字节字符集 ,比如EUC(扩展 Unix 编码 Extended Unix Code)、UTF-8 和 Mule 内部编码。所有被支持的字符集都可以被客户端透明地使用,但少数只能在服务器上使用(即作为一种服务器方编码)。默认的字符集是在使用 initdb初始化你的PostgreSQL数据库集簇时选择的。在你创建一个数据库时可以重载它,因此你可能会有多个数据库并且每一个使用不同的字符集。

    但是,一个重要的限制是每个数据库的字符集必须和数据库的LC_CTYPE(字符分类)和LC_COLLATE (字符串排序顺序)设置兼容。对于C或POSIX区域,任何字符集都是允许的,但是对于其他区域只有一种字符集可以正确工作(不过,在Windows上UTF-8编码可以和任何区域配合使用)。

    3.1. 被支持的字符集

    Table 23-1显示了PostgreSQL中可用的字符集。

    Table 23-1. PostgreSQL字符集

    并非所有的客户端API都支持上面列出的字符集。比如,PostgreSQL的JDBC 驱动就不支持MULE_INTERNAL、LATIN6、LATIN8和LATIN10。

    SQL_ASCII设置与其他设置表现得相当不同。如果服务器字符集是SQL_ASCII,服务器把字节值0-127根据 ASCII标准解释,而字节值128-255则当作无法解析的字符。如果设置为SQL_ASCII,就不会有编码转换。因此,这个设置基本不是用来声明所使用的指定编码, 因为这个声明会忽略编码。在大多数情况下,如果你使用了任何非ASCII数据,那么使用 SQL_ASCII设置都是不明智的,因为PostgreSQL将无法帮助你转换或者校验非ASCII字符。

    3.2. 设置字符集

    initdb为一个PostgreSQL集簇定义缺省的字符集(编码)。比如:

    initdb -E EUC_JP

    把缺省字符集设置为EUC_JP(用于日文的扩展Unix 编码)。如果你喜欢用长选项字符串,你可以用--encoding代替-E。 如果没有给出-E或者--encoding选项,initdb会尝试基于指定的或者默认的区域判断要使用的合适编码。

    你可以在数据库创建时指定一个非默认编码,提供的编码应和选择的区域兼容:

    createdb -E EUC_KR -T template0 --lc-collate=ko_KR.euckr --lc-ctype=ko_KR.euckr korean

    将创建一个使用EUC_KR字符集和ko_KR区域的名为korean的数据库。 另外一种实现方法是使用 SQL 命令:

    CREATE DATABASE korean WITH ENCODING 'EUC_KR' LC_COLLATE='ko_KR.euckr' LC_CTYPE='ko_KR.euckr' TEMPLATE=template0;

    注意上述命令指定拷贝template0数据库。在拷贝任何其他数据库时,不能更改从源数据库得来的编码和区域设置,因为这可能会导致破坏数据。

    数据库的编码存储在系统目录pg_database中。你可以使用psql -l选项或者l命令来查看。

    $ psql -l List of databases Name | Owner | Encoding | Collation | Ctype | Access Privileges -----------+----------+-----------+-------------+-------------+------------------------------------- clocaledb | hlinnaka | SQL_ASCII | C | C | englishdb | hlinnaka | UTF8 | en_GB.UTF8 | en_GB.UTF8 | japanese | hlinnaka | UTF8 | ja_JP.UTF8 | ja_JP.UTF8 | korean | hlinnaka | EUC_KR | ko_KR.euckr | ko_KR.euckr | postgres | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | template0 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} template1 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} (7 rows)

    Important: 在大部分现代操作系统上,PostgreSQL可以判断LC_CTYPE设置意味着哪一种字符集,并且它强制只有匹配的数据库编码被使用。在老的系统上你需要自己负责确保所使用的编码就是你所选择的区域所期望的。在这里的一个错误很可能导致区域依赖的操作产生奇怪的行为,例如排序。

    即使LC_CTYPE不是C或POSIX时,PostgreSQL将允许超级用户使用SQL_ASCII编码创建数据库。正如前文所述,SQL_ASCII并不强制存储在数据库中的数据具有任何特定的编码,并且这样这种选择存在着区域依赖的不正当行为的风险。使用这种设置组合的做法已经被废弃,并且在某天将被完全禁止。

    3.3. 服务器和客户端之间的自动字符集转换

    PostgreSQL支持一些编码在服务器和前端之间的自动编码转换。转换信息在系统目录pg_conversion中存储。PostgreSQL带着一些预定义的转换,如Table 23-2所示。你可以使用SQL命令CREATE CONVERSION创建一个新的转换。

    Table 23-2. 客户/服务器字符集转换

    要想启用自动字符集转换功能,你必须告诉PostgreSQL你想在客户端使用的字符集(编码)。你可以用好几种方法来完成:

    1.用psql里的encoding命令。encoding允许你动态修改客户端编码。比如,把编码改变为SJIS,键入:

    \encoding SJIS

    2.libpq中提供函数控制客户端编码。

    3.使用SET client_encoding TO。 可以使用这个SQL命令设置客户端编码:

    SET CLIENT_ENCODING TO 'value';

    你还可以把标准SQL语法里的SET NAMES用于这个目的:

    SET NAMES 'value';

    要查询当前客户端编码:

    SHOW client_encoding;

    要返回到缺省编码:

    RESET client_encoding;

    4.使用PGCLIENTENCODING。如果在客户端的环境里定义了PGCLIENTENCODING环境变量, 那么在与服务器进行了连接后将自动选择客户端编码(这个设置随后可以用上文提到的任何其他方法重载)。

    5.使用client_encoding配置变量。如果client_encoding变量被设置, 那么在与服务器建立了连接之后,这个客户端编码将备自动选定(这个设置随后可以用上文提到的其他方法重载)。

    假如无法进行一个特定字符的转换 — 假如你选的服务器编码是EUC_JP而 客户端是LATIN1,那么有些日文字符不能转换成LATIN1 — 将会报告一个错误。

    如果客户端字符集定义成了SQL_ASCII,那么编码转换会被禁用, 不管服务器的字符集是什么都一样。和服务器一样,除非你的工作环境全部是 ASCII 数据, 否则使用SQL_ASCII是不明智的。

    3.4. 进一步阅读

    下面是学习各种类型的编码系统的好资源。

    CJKV Information Processing: Chinese, Japanese, Korean & Vietnamese Computing

    包含对EUC_JP、 EUC_CN、EUC_KR、 EUC_TW的详细解释。

    http://www.unicode.org/

    Unicode联盟的网站。

    RFC 3629

    UTF-8 (8-bit UCS/Unicode转换格式)在这里定义。


    最新回复(0)