PostgreSQL 13中的Unicode规范化

2020-07-24 08:00:33

Unicode是一个复杂的野兽。其众多特殊特征之一是不同的码点序列可以相等。在传统编码中情况并非如此。例如,在LATIN1中,唯一等于‘a’的是‘a’,唯一等于‘ä’的是‘ä’。然而,在unicode中,带有变音符号的字符通常可以用不同的方式编码(取决于特定的字符):或者像在传统编码(如latin1中)中那样,作为预先合成的字符进行编码,或者被分解,在这里由基本字符‘a’后跟变音符号◌̈组成。这称为正则等价。拥有这两个选项的好处是,一方面,您可以轻松地从传统编码转换字符,另一方面,不需要将每个重音组合作为单独的字符添加到Unicode中。但是这个方案使得使用Unicode的软件变得更加困难。

只要您只是查看结果字符,例如在浏览器中,您就不会注意到不同之处,这对您来说无关紧要。但是,在搜索和排序字符串是基本功能和性能关键功能的数据库系统中,事情可能会变得复杂。

首先,正在使用的校正库需要意识到这一点。但是,包括glibc在内的大多数System C库都不是。所以在glibc中,当你寻找‘ä’的时候,你找不到‘ä’。看到我在那里做了什么吗?第二个代码的编码不同,但在您阅读时可能看起来是相同的。(至少我是这样进入的。它可能在您的浏览器使用过程中的某个地方被更改了。)。令人困惑。如果您使用ICU进行排序,则这是有效的,并且完全受支持。

其次,当PostgreSQL比较字符串是否相等时,它只比较字节,而不考虑同一字符串可以不同方式表示的可能性。在使用Unicode时,这在技术上是错误的,但这是必要的性能优化。要解决这个问题,您可以使用不确定排序规则,这是PostgreSQL 12中引入的一项功能。以这种方式声明的排序规则不仅会比较字节数,还会执行任何必要的预处理,以便能够比较或散列可能以不同方式编码的字符串。示例:

因此,虽然对某些Unicode字符进行编码有不同的有效方法,但有时将它们全部转换为一致的格式是很有用的。这就是所谓的正规化。有两种标准化形式:完全合成,意味着我们尽可能地将所有码点序列转换为预先合成的字符;完全分解,意味着我们尽可能地将所有码点转换为它们的组成部分(字母和重音)。在Unicode术语中,这些格式分别称为NFC和NFD。这里还有一些更多的细节,比如把所有的组合字符放到一个规范的顺序中,但这是大体上的想法。关键是,当您将Unicode字符串转换为其中一种规范化形式时,您可以逐个字节地比较或散列它们,而不必担心编码变体。您使用哪一种并不重要,只要整个系统都同意其中一种即可。

实际上,世界上大部分地区都在使用NFC。此外,许多系统都有缺陷,因为它们不能正确处理非NFC Unicode,包括大多数C库的排序工具,甚至在缺省情况下不能处理PostgreSQL,如上所述。因此,确保所有Unicode都转换为NFC是确保更好的互操作性的好方法。

PostgreSQL13现在包含两个处理Unicode规范化的新工具:一个用于测试规范化,另一个用于转换为规范化形式。例如:

SELECT';FOO';是NFC规格化;SELECT';FOO';是NFD规格化;SELECT';FOO';是规格化;--NFC是默认选择规格化(';FOO';,NFC);选择规格化(';FOO';,NFD);选择规格化(';FOO';);--NFC是默认值。

请注意,任意文本的规范化并不完全便宜。因此,只在真正重要的地方明智地应用这一点。

另请注意,规范化在串联下不会关闭。这意味着追加两个规格化字符串并不总是会产生规格化字符串。因此,即使您仔细应用这些函数,并检查您的系统是否只使用规范化的字符串,它们在合法操作期间仍然可以“潜入”。因此,仅仅假设不会发生非规范化字符串将是失败的;这个问题必须得到妥善处理。

还有另一个标准化的用例。Unicode包含一些字母和其他字符的替代形式,用于各种传统和兼容目的。例如,您可以编写FAKTURE:

现在假设您的应用程序分配了用户名或其他这样的标识符,并且有一个名为';somename';的用户和另一个名为';𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢';的用户。这至少会令人困惑,但可能会带来安全风险。利用这些相似性经常用于网络钓鱼攻击、虚假URL和类似问题。因此,Unicode包含两个额外的规范化形式,它们解决了这些相似性,并将这些替代形式转换为规范的基本字母。这些形式称为NFKC和NFKD。在其他方面,它们分别与NFC和NFD相同。例如:

=>;选择规格化(';𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢';,nfkc);规格化-名称。

创建域用户名作为文本检查(值为NFKC归一化或值为NFKD归一化);

Unicode等价性问题通常会被忽略,不会产生任何后果。在许多情况下,大多数数据都是NFC格式,因此不会出现问题。但是,忽略这些问题可能会导致奇怪的行为,显然会丢失数据,在某些情况下还会带来安全风险。因此,了解这些问题对于数据库设计人员很重要,本文中描述的工具可以用来处理这些问题。