PostgreSQL:排序规则“C”和“C.UTF-8”之间的区别

roo*_*099 7 postgresql collation encoding locales

在 PostgreSQL 中,排序规则CC.UTF-8?

两者都显示在pg_collation. 无论数据库的实际编码是什么,它是否可能与编码C.UTF-8相同?CUTF-8

Sol*_*zky 11

PostgreSQL 文档还有很多不足之处(只是说 ? )。

首先,只有一个特定数据库编码,所以CC.UTF-8你的UTF-8数据库使用UTF-8编码两者。

对于libc排序规则:按照约定通常排序规则名称实际上是以下结构的两部分名称:

{locale_name}.{encoding_name}

“语言环境”(即“文化”)是用于排序 ( LC_COLLATE) 和大小写 ( LC_CTYPE)的一组特定于语言的规则。尽管有时会出现重叠,但这实际上与这些数据的存储方式没有任何关系。

“编码”是数据的存储方式(即什么字节序列等同于哪个字符)。尽管有时会出现重叠,但这确实与使用该编码的任何特定语言的排序和大小写规则没有任何关系(某些编码可以被多种语言使用,这些语言的一种或两种可能具有完全不同的规则)那些地区)。

为了说明,考虑存储韩国数据:

  • ko_KR 是语言环境。
  • 适用于该语言环境的可能编码有:
    • EUC_KR (扩展 UNIX 代码-KR)
    • JOHAB
    • UHC (统一韩文代码/Windows949)
    • UTF8 (Unicode 的 8 位编码)

还要考虑以下内容,摘自“排序规则支持:libc 排序规则”文档(已添加重点):

例如,操作系统可能提供名为de_DE.utf8. initdb然后将创建一个以de_DE.utf8编码命名的排序规则UTF8......它还将创建一个带有.utf8从名称中剥离的标签的排序规则。因此,您还可以使用 name 下的排序规则de_DE,这样编写起来不那么麻烦,并使名称与编码无关...

...

在任何特定数据库中,只有使用该数据库编码的排序规则才有意义。中的其他条目pg_collation将被忽略。因此,de_DE即使在给定的数据库中它不是全局唯一的,诸如此类的剥离归类名称也可以被认为是唯一的。建议使用剥离的排序规则名称,因为如果您决定更改为另一种数据库编码,则需要更改的内容会减少。然而,需要注意的是defaultCPOSIX排序规则可以不考虑数据库编码的使用。

意思是,在使用 UTF-8 编码的数据库中,en_US并且en_US.UTF8是等效的。但是,在该数据库和使用LATIN1编码的数据库之间,en_US排序规则并不等效。

那么,这是否意味着CC.UTF-8相同?

不,那太容易了!!!C核对是一个例外上述行为。该C排序规则是一组简单的规则,可无论数据库的编码,而行为应在整个编码是一致的(这是可能只承认美国的英文字母- “AZ”和“AZ” -作为“字母” ,并按字节值排序,这对于您可用的编码应该是相同的)。

C.UTF-8核对实际上是一个略微增强的规则组,相比于基础C规则。这种差异实际上可以看到pg_collation,因为该值collcollatecollctype列的行,之间的不同CC.UTF-8

我将一组测试查询放在一起,以说明这两个排序规则之间的一些异同,以及与en_GB(和隐式en_GB.utf8)进行比较。我从Daniel Vérité 的回答中提供的查询开始,对它们进行了增强,希望能够更清楚地了解显示和未显示的内容,并添加了一些查询。结果表明:

  1. C并且C.UTF-8实际上是不同的规则集,即使只是略有不同,基于它们在(最终查询)中的collcollatecollctype列中的各自值pg_collation
  2. C.UTF-8 扩展被视为“字母”的字符
  3. C.UTF-8,不同于C(但像en_GB),识别无效的 Unicode 代码点(即 U+0378)并将它们排序到顶部
  4. C.UTF-8, 像C(但不同于en_GB),按代码点对非美国英语字母字符进行排序
  5. ucs_basic似乎相当于C(在文档中说明)

您可以在以下位置找到并执行查询:db<>fiddle


Dan*_*ité 5

C.UTF-8 是否与编码 UTF-8 的 C 相同?

不。例如,在 Debian 10 Linux 上考虑 UTF-8 数据库中的这些差异:

postgres=# select upper('é' collate "C"), upper('é' collate "C.UTF-8");
 upper | upper 
-------+-------
 é     | É
(1 row)

postgres=# select ('A' < E'\u0378' collate "C"),
                  ('A' < E'\u0378' collate "C.UTF-8");
 ?column? | ?column? 
----------+----------
 t        | f
(1 row)
Run Code Online (Sandbox Code Playgroud)

(U+0378 不对应于 Unicode 中的任何有效字符)。

另一个具有有效 Unicode 字符的示例(左侧是'THUMBS UP SIGN' U+1F44D):

=> select '' < 'A' collate "C";
 ?column? 
----------
 f
(1 row)

=> select '' < 'A' collate "C.UTF-8";
 ?column? 
----------
 t
(1 row)
Run Code Online (Sandbox Code Playgroud)

lc_collate是“C”(或“POSIX”)时,比较由 PostgreSQL 内部完成。在这种情况下,它使用 比较字符串的字节表示memcmp

在 libc 是提供者 ( collprovider='c'in pg_collation)的其他情况下,比较是由strcoll_lC 库完成的,因此 PostgreSQL 本身不对结果负责,并且如上面的反例所示,没有理由相信它将是相同的。

至少对于 libc 支持的排序规则来说是这样。从 Postgres 版本 10 开始,可以使用 ICU 排序规则。这些排序规则跨操作系统是一致的。

血腥的细节可以在backend/utils/adtvarlena.c的源代码中找到,尤其是varstrmp_cmp函数。