閱讀722 返回首頁    go 技術社區[雲棲]


PostgreSQL服務器管理:本地化

本文檔為PostgreSQL 9.6.0文檔,本轉載已得到原譯者彭煜瑋授權。

區域支持指的是應用遵守文化偏好的問題,包括字母表、排序、數字格式等。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,但是其原則是相同的。

有時候,把幾種區域規則混合起來也很有用,比如,使用英語排序規則而用西班牙語消息。 為了支持這些,我們有一套區域子類用於控製本地化規則的某些方麵:


image


這些類名轉換成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或者向開發者郵遞列表發郵件。

排序規則特性允許指定每一列甚至每一個操作的數據的排序順序和字符分類行為。這放鬆了數據庫的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排序規則具有相同的行為。因此,我們不推薦混合使用被剝離的和非被剝離的排序規則名。

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字符集

12


並非所有的客戶端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. 客戶/服務器字符集轉換


13

要想啟用自動字符集轉換功能,你必須告訴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的詳細解釋。

https://www.unicode.org/

Unicode聯盟的網站。

RFC 3629

UTF-8 (8-bit UCS/Unicode轉換格式)在這裏定義。

最後更新:2017-08-22 17:32:32

  上一篇:go  唿叫中心雲服務助力傳統企業渡過轉型期
  下一篇:go  MQC功能測試大揭秘(1) - 從Android自動化測試談起