-
2023-10-20 PostgreSQL
icuロケールプロバイダとlibcロケールプロバイダの違いを確認
icuロケールプロバイダとlibcロケールプロバイダについて
PostgreSQL 16 で、icu ロケールプロバイダのサポートがデフォルトで有効となりました。
当初(beta1 前後?)は icu ロケールプロバイダがデフォルトのロケールプロバイダになる可能性もあったようですが、見送られたようです。
ただ、PostgreSQL 16 で見送られただけですので、PostgreSQL17 以降で icu ロケールプロバイダがデフォルトのロケールプロバイダとなる可能性があります。
本記事では、その icu ロケールプロバイダについて確認した内容をお知らせしたいと思います。
前回の記事( https://www.insight-ltd.co.jp/tech_blog/postgresql/748/ )も、icu ロケールプロバイダについての記事となっていますので、よろしければ合わせてご参照ください。
icu ロケールプロバイダとは
そもそも icu ロケールプロバイダとは何か、という疑問が湧きますが、WEB 検索してもあまり明言されている情報がありません。
断片的な情報を集めた結果、(ざっくりとした説明ですが)icu ロケールプロバイダは International Components for Unicode という名前(意味)で、国際標準に対応したロケールを提供するロケールプロバイダということが分かりました。
libc ロケールプロバイダとは
では、現在の PostgreSQL のデフォルトのロケールプロバイダである、libc ロケールプロバイダについても知りたくなってくるかと思います。
これは公式ドキュメントに
照合順序の定義には、ロケールデータを提供するライブラリを指定するプロバイダ(provider)が含まれます。 標準プロバイダの一つはlibcで、オペレーティングシステムのCライブラリが提供するロケールを使用します
という説明がありました。
■ PostgreSQL16 公式ドキュメント - 24.2.2. 照合順序の管理 -
https://www.postgresql.jp/document/15/html/collation.html#COLLATION-MANAGING
icuロケールプロバイダとlibcロケールプロバイダの相違点
ここで本題の「 icu ロケールプロバイダと libc ロケールプロバイダは何が違うのか」について、確認していきます。
前述のロケールプロバイダの説明をまとめますと
ロケールプロバイダ | 説明 |
---|---|
icu | 国際標準に対応したロケールを提供 |
libc | OS の C ライブラリで定義されたロケールを提供 |
ということになります。
PostgreSQL では、主にソート順や通貨の表示などがロケールの設定によって変動します。
以下では、ロケールプロバイダが icu と libc のデータベースクラスタをそれぞれ用意し、ロケールを ja_JP.UTF8 とした状態でソート順に違いがあるのかを確認します。
ロケールプロバイダ = icu, ロケール = ja_JP.UTF8
ロケールプロバイダを icu、ロケールを ja_JP.UTF8 にしたデータベースクラスタを作成します。
$ initdb --locale-provider=icu --icu-locale=ja_JP.UTF8 --locale=ja_JP.UTF8 -D ./data_icu_ja
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
postgres | postgres | UTF8 | icu | ja_JP.UTF8 | ja_JP.UTF8 | ja-JP | |
template0 | postgres | UTF8 | icu | ja_JP.UTF8 | ja_JP.UTF8 | ja-JP | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | icu | ja_JP.UTF8 | ja_JP.UTF8 | ja-JP | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(3 rows)
想定通りにデータベースクラスタが作成されていることを確認しました。
サンプル文字列を格納した sort_table に対して SELECT 文を実行し、文字列の並びを確認します。
postgres=# SELECT * FROM sort_table ORDER BY c;
c | type
----+--------------------------
_ | 記号
! | 記号
0 | 数字
1 | 数字
9 | 数字
a | 半角小文字アルファベット
a | 全角小文字アルファベット
A | 半角大文字アルファベット
A | 全角大文字アルファベット
b | 半角小文字アルファベット
b | 全角小文字アルファベット
B | 半角大文字アルファベット
B | 全角大文字アルファベット
C | 半角大文字アルファベット
z | 半角小文字アルファベット
z | 全角小文字アルファベット
Z | 全角大文字アルファベット
ぁ | 小文字ひらがな
ァ | 半角小文字カタカナ
あ | ひらがな
ア | カタカナ
ア | 半角カタカナ
ぃ | 小文字ひらがな
ィ | 半角小文字カタカナ
い | ひらがな
イ | カタカナ
イ | 半角カタカナ
ぅ | 小文字ひらがな
ゥ | 半角小文字カタカナ
ウ | 半角カタカナ
か | ひらがな
カ | カタカナ
さ | ひらがな
サ | カタカナ
漢 | 漢字
字 | 漢字
(36 rows)
icu ロケールプロバイダの ja_JP.UTF8 では
- 記号
- 数値
- アルファベット
- ひらがな・カタカナ
- 漢字
の順に並びました。
その他にも
- アルファベット、ひらがな・カタカナは、半角全角や小文字大文字に関係なく「あいうえお順」に並びました。
- アルファベットは、同じ文字であれば「半角小文字 → 全角小文字 → 半角大文字 → 全角大文字」に並びました。
- 例)a → a → A → A → b → b ...
- ひらがな・カタカナは、同じ文字であれば「小文字ひらがな → 半角小文字カタカナ → ひらがな → カタカナ →半角カタカナ」の順に並びました。
- 例)ぁ → ァ → あ → ア → ア → ぃ → ィ ...
ということが分かりました。
ロケールプロバイダ = libc, ロケール = ja_JP.UTF8
比較対象として、ロケールプロバイダを libc、ロケールを ja_JP.UTF8 にしたデータベースクラスタを作成します。
$ initdb --locale-provider=libc --locale=ja_JP.UTF8 -D ./data_libc_ja
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
postgres | postgres | UTF8 | libc | ja_JP.UTF8 | ja_JP.UTF8 | | |
template0 | postgres | UTF8 | libc | ja_JP.UTF8 | ja_JP.UTF8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | ja_JP.UTF8 | ja_JP.UTF8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(3 rows)
こちらも想定通りにデータベースクラスタが作成されていることを確認しました。
サンプル文字列を格納した sort_table に対して SELECT 文を実行し、文字列の並びを確認します。
postgres=# SELECT * FROM sort_table ORDER BY c;
c | type
----+--------------------------
! | 記号
0 | 数字
1 | 数字
9 | 数字
A | 半角大文字アルファベット
B | 半角大文字アルファベット
C | 半角大文字アルファベット
_ | 記号
a | 半角小文字アルファベット
b | 半角小文字アルファベット
z | 半角小文字アルファベット
ァ | 半角小文字カタカナ
ィ | 半角小文字カタカナ
ゥ | 半角小文字カタカナ
ア | 半角カタカナ
イ | 半角カタカナ
ウ | 半角カタカナ
A | 全角大文字アルファベット
B | 全角大文字アルファベット
Z | 全角大文字アルファベット
a | 全角小文字アルファベット
b | 全角小文字アルファベット
z | 全角小文字アルファベット
ぁ | 小文字ひらがな
あ | ひらがな
ぃ | 小文字ひらがな
い | ひらがな
ぅ | 小文字ひらがな
か | ひらがな
さ | ひらがな
ア | カタカナ
イ | カタカナ
カ | カタカナ
サ | カタカナ
漢 | 漢字
字 | 漢字
(36 rows)
libc ロケールプロバイダの ja_JP.UTF8 では
- 記号(!)
- 数値
- 半角大文字アルファベット
- 記号(_)
- 半角小文字アルファベット
- 半角小文字カタカナ
- 半角カタカナ
- 全角大文字アルファベット
- 全角小文字アルファベット
- ひらがな
- カタカナ
- 漢字
の順に並びました。
その他にも
- ひらがなは「あいうえお順」に並んでいるようですが、同じ文字であれば「小文字ひらがな → 大文字ひらがな」の順に並びました。
- 例)ぁ → あ → ぃ → い ...
ということが分かりました。
C ロケール(ロケールなし)では icu と libc でソート順に違いはあるのかを確認
ja_JP.UTF8 では、icu ロケールプロバイダと libc ロケールプロバイダで、ソート順に相違がありました。
ロケールなしと呼ばれる C ロケールでもソート順に相違があるのかを確認したいと思います。
ただ、データベースのロケールは変更できませんので、データベースを新たに作成して検証する必要となります。
ロケールは template0 からデータベースを作成するときのみ指定できるようですので、下記のような CREATE DATABASE 文を実行し、C ロケールのデータベースを作成しました。
postgres=# CREATE DATABASE c_locale_db LC_COLLATE 'C' LC_CTYPE 'C' ENCODING 'UTF8' TEMPLATE template0;
CREATE DATABASE
ロケールプロバイダ = icu, ロケール = C
c_locale_db=# \l c_locale_db
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-------------+----------+----------+-----------------+---------+-------+------------+-----------+-------------------
c_locale_db | postgres | UTF8 | icu | C | C | ja-JP | |
(1 row)
想定通りに C ロケールでデータベースが作成されていることを確認しました。
サンプル文字列を格納した sort_table に対して SELECT 文を実行し、文字列の並びを確認します。
c | type
----+--------------------------
_ | 記号
! | 記号
0 | 数字
1 | 数字
9 | 数字
a | 半角小文字アルファベット
a | 全角小文字アルファベット
A | 半角大文字アルファベット
A | 全角大文字アルファベット
b | 半角小文字アルファベット
b | 全角小文字アルファベット
B | 半角大文字アルファベット
B | 全角大文字アルファベット
C | 半角大文字アルファベット
z | 半角小文字アルファベット
z | 全角小文字アルファベット
Z | 全角大文字アルファベット
ぁ | 小文字ひらがな
ァ | 半角小文字カタカナ
あ | ひらがな
ア | カタカナ
ア | 半角カタカナ
ぃ | 小文字ひらがな
ィ | 半角小文字カタカナ
い | ひらがな
イ | カタカナ
イ | 半角カタカナ
ぅ | 小文字ひらがな
ゥ | 半角小文字カタカナ
ウ | 半角カタカナ
か | ひらがな
カ | カタカナ
さ | ひらがな
サ | カタカナ
漢 | 漢字
字 | 漢字
(36 rows)
icu ロケールプロバイダの C ロケール では
- 記号
- 数値
- アルファベット
- ひらがな・カタカナ
- 漢字
の順に並びました。
その他にも
- アルファベットは「ABC順」に並んでいるようですが、同じ文字では「半角小文字 → 全角小文字 → 半角大文字 → 全角大文字」の順に並びました。
- 例)a → a → A → A → b ...
- ひらがな・カタカナは「あいうえお順」に並んでいるようですが、同じ文字であれば「小文字ひらがな → 半角小文字カタカナ → 大文字ひらがな → 全角カタカナ → 半角カタカナ」の順に並びました。
- 例)ぁ → ァ → あ → ア → ア → ぃ → ィ ...
ということが分かりました。
ロケールプロバイダ = libc, ロケール = C
c_locale_db=# \l c_locale_db
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-------------+----------+----------+-----------------+---------+-------+------------+-----------+-------------------
c_locale_db | postgres | UTF8 | libc | C | C | | |
(1 row)
こちらも想定通りに C ロケールでデータベースが作成されていることを確認しました。
サンプル文字列を格納した sort_table に対して SELECT 文を実行し、文字列の並びを確認します。
c_locale_db=# SELECT * FROM sort_table ORDER BY c;
c | type
----+--------------------------
! | 記号
0 | 数字
1 | 数字
9 | 数字
A | 半角大文字アルファベット
B | 半角大文字アルファベット
C | 半角大文字アルファベット
_ | 記号
a | 半角小文字アルファベット
b | 半角小文字アルファベット
z | 半角小文字アルファベット
ぁ | 小文字ひらがな
あ | ひらがな
ぃ | 小文字ひらがな
い | ひらがな
ぅ | 小文字ひらがな
か | ひらがな
さ | ひらがな
ア | カタカナ
イ | カタカナ
カ | カタカナ
サ | カタカナ
字 | 漢字
漢 | 漢字
A | 全角大文字アルファベット
B | 全角大文字アルファベット
Z | 全角大文字アルファベット
a | 全角小文字アルファベット
b | 全角小文字アルファベット
z | 全角小文字アルファベット
ァ | 半角小文字カタカナ
ィ | 半角小文字カタカナ
ゥ | 半角小文字カタカナ
ア | 半角カタカナ
イ | 半角カタカナ
ウ | 半角カタカナ
(36 rows)
libc ロケールプロバイダの C ロケール では
- 記号(!)
- 数値
- 半角大文字アルファベット
- 記号(_)
- 半角小文字アルファベット
- ひらがな
- カタカナ
- 漢字
- 全角大文字アルファベット
- 全角小文字アルファベット
- 半角小文字カタカナ
- 半角カナ
の順に並びました。
その他にも
- ひらがなは「あいうえお順」に並んでいるようですが、同じ文字であれば「小文字ひらがな → 大文字ひらがな」の順に並びました。
- 例)ぁ → あ → ぃ → い ...
ということが分かりました。
ロケールプロバイダとロケールのソート順
検証したソート順をまとめると下記の表のとおりになります。
ソートの優先度 | icu の ja_JP.UTF8 | libc の ja_JP.UTF8 | icu の C | libc の C |
---|---|---|---|---|
1 | 記号 | 記号 (!) | 記号 | 記号(!) |
2 | 数値 | 数値 | 数値 | 数値 |
3 | アルファベット | 半角大文字アルファベット | アルファベット | 半角大文字アルファベット |
4 | ひらがな・カタカナ | 記号 (_) | ひらがな・カタカナ | 記号(_) |
5 | 漢字 | 半角小文字アルファベット | 漢字 | 半角小文字アルファベット |
6 | ー | 半角小文字カタカナ | ー | ひらがな |
7 | ー | 半角カタカナ | ー | カタカナ |
8 | ー | 全角大文字アルファベット | ー | 漢字 |
9 | ー | 全角小文字アルファベット | ー | 全角大文字アルファベット |
10 | ー | ひらがな | ー | 全角小文字アルファベット |
11 | ー | カタカナ | ー | 半角小文字カタカナ |
12 | ー | 漢字 | ー | 半角カタカナ |
- icu ロケールプロバイダでは、ja_JP.UTF8 と C ロケールにソート順の相違はないようです。
- C ロケール = ロケールなし でも、icu ロケールプロバイダと libc ロケールプロバイダで、ソート順に相違がありました。
- libc の「半角小文字アルファベット」までは、UNICODE のコード順に並んでいるようです。
まとめ
少し長くなりましたが、icu ロケールプロバイダと libc ロケールプロバイダの相違点などを確認しました。
ロケールプロバイダが違うことで、思ったよりもソート順が変動しましたので驚きました。
特に C ロケールの場合には、ロケールプロバイダが違ってもソート順は同じになるという予測でしたが、そうではありませんでした。
また、今回は紹介しきれませんでしたが、データベースクラスタのロケールプロバイダが libc であっても、ビルド時に icu サポートを有効にしておけば、(下記に記載した SQL のように)COLLATE 句を指定することで icu ロケールプロバイダが提供するソート順を利用することができます。
SELECT * FROM sort_table ORDER BY c COLLATE "und-x-icu";
正直なところロケールについて、まだ仕組みや仕様を完全に理解できているわけではありませんので、引き続き検証や情報収集を行っていきたいと思います。