sql >> データベース >  >> RDS >> PostgreSQL

PostgreSQL13でのUnicode正規化

    ユニコード等価性

    Unicodeは複雑な獣です。その多数の特有の機能の1つは、コードポイントの異なるシーケンスが等しくなる可能性があることです。これは、レガシーエンコーディングには当てはまりません。たとえば、LATIN1では、「a」に等しいのは「a」だけであり、「ä」に等しいのは「ä」だけです。ただし、Unicodeでは、発音区別符号付きの文字は、多くの場合(特定の文字に応じて)さまざまな方法でエンコードできます。LATIN1などの従来のエンコードで行われたように合成済み文字として、またはベース文字'aで構成される分解された文字としてエンコードできます。 'ここに発音区別符号◌̈が続きます。これは正規の同等性と呼ばれます 。これらのオプションの両方を使用する利点は、一方では従来のエンコーディングから文字を簡単に変換でき、他方ではすべてのアクセントの組み合わせを個別の文字としてUnicodeに追加する必要がないことです。しかし、このスキームは、Unicodeを使用するソフトウェアにとって事態をより困難にします。

    ブラウザなどで結果の文字を見ているだけであれば、違いに気付くことはなく、これは問題ではありません。ただし、文字列の検索と並べ替えが基本的でパフォーマンスが重要な機能であるデータベースシステムでは、事態が複雑になる可能性があります。

    まず、使用中の照合ライブラリはこれを認識する必要があります。ただし、glibcを含むほとんどのシステムCライブラリはそうではありません。したがって、glibcでは、「ä」を検索しても「ä」は見つかりません。私がそこで何をしたかわかりますか? 2つ目はエンコードが異なりますが、読んでいる人にはおそらく同じように見えます。 (少なくともそれは私がそれを入力した方法です。それはあなたのブラウザへの途中でどこかで変更されたかもしれません。)紛らわしい。照合にICUを使用する場合、これは機能し、完全にサポートされています。

    第2に、PostgreSQLが文字列の同等性を比較する場合、バイトを比較するだけであり、同じ文字列を異なる方法で表現できる可能性は考慮されていません。 Unicodeを使用する場合、これは技術的に間違っていますが、必要なパフォーマンスの最適化です。これを回避するには、非決定論的照合を使用できます。 、PostgreSQL12で導入された機能。そのように宣言された照合はしない バイトを比較するだけです ただし、さまざまな方法でエンコードされている可能性のある文字列を比較またはハッシュできるようにするために、必要な前処理を実行します。例:

    CREATE COLLATION ndcoll (provider = icu, locale = 'und', deterministic = false);

    正規化フォーム

    したがって、特定のUnicode文字をエンコードするためのさまざまな有効な方法がありますが、それらすべてを一貫した形式に変換すると便利な場合があります。これは正規化と呼ばれます 。 2つの正規化フォームがあります :完全に構成された 、つまり、すべてのコードポイントシーケンスを可能な限り合成済み文字に変換し、完全に分解します。 、つまり、すべてのコードポイントを可能な限りそれらのコンポーネント部分(文字とアクセント)に変換します。 Unicodeの用語では、これらの形式はそれぞれNFCおよびNFDとして知られています。これには、結合文字をすべて正規の順序にするなど、さらに詳細がありますが、これが一般的な考え方です。重要なのは、Unicode文字列を正規化形式の1つに変換すると、エンコードのバリエーションを気にすることなく、それらをバイト単位で比較またはハッシュできるということです。システム全体が1つに同意する限り、どちらを使用してもかまいません。

    実際には、世界のほとんどがNFCを使用しています。さらに、多くのシステムは、NFC以外のUnicodeを正しく処理しないという点で欠陥があります。これには、前述のように、ほとんどのCライブラリの照合機能やデフォルトのPostgreSQLも含まれます。したがって、すべてのUnicodeがNFCに変換されるようにすることは、相互運用性を向上させるための良い方法です。

    PostgreSQLでの正規化

    PostgreSQL 13には、Unicode正規化を処理するための2つの新しい機能が含まれています。1つは正規化をテストする関数で、もう1つは正規化形式に変換する関数です。例:

    SELECT 'foo' IS NFC NORMALIZED;
    SELECT 'foo' IS NFD NORMALIZED;
    SELECT 'foo' IS NORMALIZED;  -- NFC is the default
    
    SELECT NORMALIZE('foo', NFC);
    SELECT NORMALIZE('foo', NFD);
    SELECT NORMALIZE('foo');  -- NFC is the default

    (構文はSQL標準で指定されています。)

    1つのオプションは、ドメインでこれを使用することです。例:

    CREATE DOMAIN norm_text AS text CHECK (VALUE IS NORMALIZED);

    任意のテキストの正規化は完全に安価ではないことに注意してください。したがって、これを賢明に、そしてそれが本当に重要な場所にのみ適用してください。

    正規化は連結では閉じられないことにも注意してください。つまり、2つの正規化された文字列を追加しても、必ずしも正規化された文字列になるとは限りません。したがって、これらの関数を注意深く適用し、システムが正規化された文字列のみを使用していることを確認した場合でも、正当な操作中に「忍び寄る」可能性があります。したがって、正規化されていない文字列が発生しないと仮定するだけでは失敗します。この問題は適切に処理する必要があります。

    互換性のある文字

    正規化には別のユースケースがあります。 Unicodeには、さまざまなレガシーおよび互換性の目的で、いくつかの代替形式の文字およびその他の文字が含まれています。たとえば、Fraktur:

    と書くことができます。
    SELECT '𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢';

    ここで、アプリケーションがユーザー名または他のそのような識別子を割り当て、'somename'という名前のユーザーがいると想像してください。 もう1つは'𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢'という名前です 。これは少なくとも混乱を招く可能性がありますが、おそらくセキュリティ上のリスクがあります。このような類似性を悪用することは、フィッシング攻撃、偽のURL、および同様の懸念事項でよく使用されます。したがって、Unicodeには、これらの類似点を解決し、そのような代替形式を正規の基本文字に変換する2つの追加の正規化形式が含まれています。これらの形式はNFKCおよびNFKDと呼ばれます。それ以外は、それぞれNFCおよびNFDと同じです。例:

    => select normalize('𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢', nfkc);
     normalize
    -----------
     somename
    

    繰り返しになりますが、おそらくドメインの一部としてチェック制約を使用すると便利です:

    CREATE DOMAIN username AS text CHECK (VALUE IS NFKC NORMALIZED OR VALUE IS NFKD NORMALIZED);

    (実際の正規化は、おそらくユーザーインターフェイスのフロントエンドで行う必要があります。)

    このような懸念に対処するための文字列の処理については、RFC3454も参照してください。

    概要

    Unicodeの等価性の問題は、結果なしに無視されることがよくあります。多くの場合、ほとんどのデータはNFC形式であるため、問題は発生しません。ただし、これらの問題を無視すると、奇妙な動作、明らかにデータの欠落、場合によってはセキュリティリスクが発生する可能性があります。したがって、これらの問題を認識することはデータベース設計者にとって重要であり、この記事で説明するツールを使用してそれらに対処することができます。


    1. SQL Serverのすべてのデータベースのすべてのテーブルを単一の結果セットに一覧表示するにはどうすればよいですか?

    2. 連想テーブルを使用するよりも、フラグをビットマスクとして保存する方がよいのはいつですか。

    3. SQLServer診断を実行するための4つのヒント

    4. SQLトレースと拡張イベントの「オブザーバーオーバーヘッド」の測定