MathJax

MathJax-2

MathJax-3

Google Code Prettify

置頂入手筆記

EnterproseDB Quickstart — 快速入門筆記

由於考慮採用 EnterpriseDB 或是直接用 PostgreSQL 的人,通常需要一些入手的資料。這邊紀錄便提供相關快速上手的簡單筆記 ~ 這篇筆記以 資料庫安裝完畢後的快速使用 為目標,基本紀錄登入使用的範例:

2018年10月2日 星期二

在 Postgres 10 擴增 Big5 中文難字 — 從原始碼開始(用 Big5-2003 字碼集)

PostgreSQL 不支援「直接」儲存 Big5 編碼(聽說很古老的版本有支援,但我不清楚...),因此目前的 PGSQL 資料庫支援 Big5 的方式,是把裡面存放的 UTF8 編碼資料,利用內建編碼表,轉換成 Big5 編碼,再把資料送往 Client 端。如果前端送來的是 Big5 編碼,指名 client_encoding 之後,PGSQL 就會進行編碼轉換,以 UTF8 存放到資料庫裡面。

由於 Postgres 會嚴謹檢查資料的規格,不符的會拒絕吃進來,因此遇到沒有內建的 Big5 ↔ UTF8 編碼,會拒絕該筆資料,回傳錯誤。

PGSQL 10 支援的是標準字區,以及 CP950 (可以到 Unicode 官方網站找到 CP950 原始對照表)的 0xf9d6-0xf9d 部份(這件事寫在原始碼的 UCS_to_BIG5.pl 這份 Perl Script 裡面)。在這個範圍之外的 Big5 字碼,對 PGSQL 都是從缺,沒有對應。

但是,上述被拒絕的狀況,在台灣偏偏是常常發生...原因是,過往很多系統都會有自造 Big5 字碼,通常會依照各自單位的內部規則,這些自造字用的字碼區,偏偏又在 PGSQL 不認得的區段,所以會發生錯誤..

因此,這裡要紀錄的是,如何從原始碼擴增字碼,讓 PGSQL 認得自造 Big5 字碼。

擴增的方式,是增加編碼對應,讓編碼在轉換時,認得這些對應編碼。

步驟一:準備好貴單位獨特的 Big5 ↔ Unicode 編碼表,並下載 PostgreSQL 原始碼
編碼表的格式參照 Unicode 官方提供的 BIG5.TXT 格式。
[user@dockerhost pgsql_build]$ wget https://moztw.org/docs/big5/table/big5_2003-b2u.txt
[user@dockerhost pgsql_build]$ mv big5_2003-b2u.txt BIG5.TXT
[user@dockerhost pgsql_build]$ wget https://ftp.postgresql.org/pub/source/v10.4/postgresql-10.4.tar.gz
[user@dockerhost pgsql_build]$ tar -xvf postgresql-10.4.tar.gz

步驟二:執行原始碼裡面,用來生成字碼對照表的 Script,產生 PostgreSQL 的編碼表
以下過程,會發現對照錯誤,分別是 F9DD(╔),F9FA(╗),F9DF(╚),F9FB(╝) 四個 Big5 字碼,由於我不知道的原因,他們四個的編碼在轉過去跟轉回來有些不同。以下是 Big5-2003 相關網站的描述
使用上要注意 F9DD/F9FA 對到 U+2554, F9DF/F9FB 對到 U+2557, F9E3/F9FC 對到 U+255A, F9E5/F9FD 對到 U+255D. 從 unicode 轉回 big5 分別對到 F9DD,F9DF,F9E3,F9E5 比較正確.
[user@dockerhost pgsql_build]$ ls ./postgresql-10.4/src/backend/utils/mb/Unicode/
big5_to_utf8.map            UCS_to_BIG5.pl            utf8_to_iso8859_7.map
convutils.pm                UCS_to_EUC_CN.pl          utf8_to_iso8859_8.map
euc_cn_to_utf8.map          UCS_to_EUC_JIS_2004.pl    utf8_to_iso8859_9.map
euc-jis-2004-std.txt        UCS_to_EUC_JP.pl          utf8_to_johab.map
euc_jis_2004_to_utf8.map    UCS_to_EUC_KR.pl          utf8_to_koi8r.map
euc_jp_to_utf8.map          UCS_to_EUC_TW.pl          utf8_to_koi8u.map
euc_kr_to_utf8.map          UCS_to_GB18030.pl         utf8_to_shift_jis_2004.map
euc_tw_to_utf8.map          UCS_to_JOHAB.pl           utf8_to_sjis.map
gb-18030-2000.xml           UCS_to_most.pl            utf8_to_uhc.map
gb18030_to_utf8.map         UCS_to_SHIFT_JIS_2004.pl  utf8_to_win1250.map
gbk_to_utf8.map             UCS_to_SJIS.pl            utf8_to_win1251.map
iso8859_10_to_utf8.map      UCS_to_UHC.pl             utf8_to_win1252.map
iso8859_13_to_utf8.map      uhc_to_utf8.map           utf8_to_win1253.map
iso8859_14_to_utf8.map      utf8_to_big5.map          utf8_to_win1254.map
iso8859_15_to_utf8.map      utf8_to_euc_cn.map        utf8_to_win1255.map
iso8859_16_to_utf8.map      utf8_to_euc_jis_2004.map  utf8_to_win1256.map
iso8859_2_to_utf8.map       utf8_to_euc_jp.map        utf8_to_win1257.map
iso8859_3_to_utf8.map       utf8_to_euc_kr.map        utf8_to_win1258.map
iso8859_4_to_utf8.map       utf8_to_euc_tw.map        utf8_to_win866.map
iso8859_5_to_utf8.map       utf8_to_gb18030.map       utf8_to_win874.map
iso8859_6_to_utf8.map       utf8_to_gbk.map           win1250_to_utf8.map
iso8859_7_to_utf8.map       utf8_to_iso8859_10.map    win1251_to_utf8.map
iso8859_8_to_utf8.map       utf8_to_iso8859_13.map    win1252_to_utf8.map
iso8859_9_to_utf8.map       utf8_to_iso8859_14.map    win1253_to_utf8.map
johab_to_utf8.map           utf8_to_iso8859_15.map    win1254_to_utf8.map
koi8r_to_utf8.map           utf8_to_iso8859_16.map    win1255_to_utf8.map
koi8u_to_utf8.map           utf8_to_iso8859_2.map     win1256_to_utf8.map
Makefile                    utf8_to_iso8859_3.map     win1257_to_utf8.map
shift_jis_2004_to_utf8.map  utf8_to_iso8859_4.map     win1258_to_utf8.map
sjis-0213-2004-std.txt      utf8_to_iso8859_5.map     win866_to_utf8.map
sjis_to_utf8.map            utf8_to_iso8859_6.map     win874_to_utf8.map
[user@dockerhost pgsql_build]$ mv BIG5.TXT ./postgresql-10.4/src/backend/utils/mb/Unicode/
[user@dockerhost pgsql_build]$ cd ./postgresql-10.4/src/backend/utils/mb/Unicode/
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl
READ ERROR at line 2 in BIG5.TXT: 0x8140 0xEEB8
[user@dockerhost Unicode]$ vi ./UCS_to_BIG5.pl 
[user@dockerhost Unicode]$ vi ./convutils.pm 
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl 
- Writing UTF8=>BIG5 conversion table: utf8_to_big5.map
Error: duplicate source code on BIG5.TXT:18794: 0xe29594 => 0xf9dd, 0xf9fa
[user@dockerhost Unicode]$ vi BIG5.TXT 
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl 
- Writing UTF8=>BIG5 conversion table: utf8_to_big5.map
Error: duplicate source code on BIG5.TXT:18795: 0xe29597 => 0xf9df, 0xf9fb
[user@dockerhost Unicode]$ vi BIG5.TXT 
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl 
- Writing UTF8=>BIG5 conversion table: utf8_to_big5.map
Error: duplicate source code on BIG5.TXT:18796: 0xe2959a => 0xf9e3, 0xf9fc
[user@dockerhost Unicode]$ vi BIG5.TXT 
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl 
- Writing UTF8=>BIG5 conversion table: utf8_to_big5.map
Error: duplicate source code on BIG5.TXT:18797: 0xe2959d => 0xf9e5, 0xf9fd
[user@dockerhost Unicode]$ vi BIG5.TXT 
[user@dockerhost Unicode]$ ./UCS_to_BIG5.pl 
- Writing UTF8=>BIG5 conversion table: utf8_to_big5.map
- Writing BIG5=>UTF8 conversion table: big5_to_utf8.map
[user@dockerhost Unicode]$ 

步驟三:編譯整個程式
這裡就進入 docker 環境裡面(用 docker 只是方便而已~),裝完編譯需要的套件後,直接開始編譯(這裡就不紀錄中間的輸出資訊了)
[user@dockerhost pgsql_build]$ docker run -it --volume /home/user/pgsql_build:/root/pgsql-src --name compile_env centos:7
[user@dockerhost pgsql_build]$ docker exec -it compile_env bash
[root@f563eb836ce3 /]# yum groupinstall "Development tools"
[root@f563eb836ce3 /]# yum install -y readline-devel zlib-devel
[root@f563eb836ce3 /]# cd ~/pgsql-src/postgresql-10.4
[root@f563eb836ce3 postgresql-10.4]# ./configure
[root@f563eb836ce3 postgresql-10.4]# make -j4
[root@f563eb836ce3 postgresql-10.4]# make install
Note: 到 PGSQL 11 就需要增加 LLVM 套件來編譯 JIT 部份了~

步驟四:測試轉換
初始化一個資料庫,然後用轉換函數,塞一些 Big5-2003 新增編碼區的內容(跟 WIn-CP950 相比)
這裡隨便挑,從對應表亂選了一個 Big5 編碼為 0x814E 的字碼。
不過要注意的是,雖然好像是挑了一個「蕋」字
https://www.cns11643.gov.tw/AIDB/query_general_view.do?page=3&code=5259
https://www.cns11643.gov.tw/wordView.jsp?ID=217689
http://www.fileformat.info/info/unicode/char/854b/index.htm
http://chardb.iis.sinica.edu.tw/char/U+854B

但是查看原本檔案之後,發現對應的是 U+EEC6,而不是上面寫到的 U+854B
http://www.fileformat.info/info/unicode/char/eec6/index.htm
原因是「蕋」字的對應,是 Big-5E 的結果,而不是 Big5-2003 的結果...

這大概也是為什麼 Big5-2003 後來沒有風聲的原因吧~(檢查以下結果時也嚇了一跳,還以為失敗了..)
[root@f563eb836ce3 ~]# ls /usr/local/pgsql/
bin  include  lib  share
[root@f563eb836ce3 ~]# export PATH=$PATH:/usr/local/pgsql/bin/
[root@f563eb836ce3 ~]# initdb -E utf8 -D ~/data
initdb: cannot be run as root
Please log in (using, e.g., "su") as the (unprivileged) user that will
own the server process.
[root@f563eb836ce3 ~]# 
[root@f563eb836ce3 ~]# useradd postgres
[root@f563eb836ce3 ~]# su - postgres
[postgres@f563eb836ce3 ~]$ export PATH=$PATH:/usr/local/pgsql/bin/
[postgres@f563eb836ce3 ~]$ mkdir ~/data
[postgres@f563eb836ce3 ~]$ initdb -E utf8 -D ~/data
[postgres@f563eb836ce3 ~]$ pg_ctl -D ~/data start
[postgres@f563eb836ce3 ~]$ psql
psql (10.4)
Type "help" for help.

postgres=# select convert('\x814E','big5','utf8');
 convert  
----------
 \xeebb86
(1 row)

postgres=# select version();
                                                 version                                                 
---------------------------------------------------------------------------------------------------------
 PostgreSQL 10.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28), 64-bit
(1 row)

postgres=# 
postgres=# \q
[postgres@f563eb836ce3 ~]$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[postgres@f563eb836ce3 ~]$ 

相比之下,在一般提供的 PostgreSQL,這個 Big5 編碼沒有對照的 UTF8。例如,這裡我用 AlpineLinux Postgres Docker 試一下
postgres=# select convert('\x814E','big5','utf8');
ERROR:  character with byte sequence 0x81 0x4e in encoding "BIG5" has no equivalent in encoding "UTF8"
postgres=# select version();
                                        version
---------------------------------------------------------------------------------------
 PostgreSQL 10.4 on x86_64-pc-linux-musl, compiled by gcc (Alpine 6.4.0) 6.4.0, 64-bit
(1 row)
postgres=# 


測試到此結束。

最後,在 PostgreSQL 9.6 以前版本中,上面的步驟還是適用的,只不過產生出來的內部 Mapping File 內容長的不一樣,詳細可以比較下面兩個檔案:
https://github.com/postgres/postgres/blob/REL9_6_STABLE/src/backend/utils/mb/Unicode/big5_to_utf8.map
https://github.com/postgres/postgres/blob/REL_10_STABLE/src/backend/utils/mb/Unicode/big5_to_utf8.map

感想:這種有點沒有意義的考古好麻煩,字碼問題真的是很困擾的東西。


其他參考資料

PostgreSQL 主要採用的字碼對應表
 http://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/OTHER/BIG5.TXT
Windows 擴充的 BIG5 字碼表 CP950
 Code page 950 - Wikipedia
 http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP950.TXT
 預覽「Big5 2003」 - OpenFoundry
Big5-2003
認識全字庫 - 中文碼介紹 - CNS11643 中文全字庫
BCC-16 (in Chinese) 計算機概論十六講 POOL 1.09 Chinese Codes: CNS-11643, Big-5, CCCII
BCC-16 (in Chinese) 計算機概論十六講 POOL 1.10 Unicode

沒有留言:

張貼留言