由於 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 installNote: 到 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
沒有留言:
張貼留言