PostgreSQL 中的一字节“char”类型究竟是如何工作的?

Eva*_*oll 9 postgresql datatypes storage cast

我经常看到人们谈论"char"。我从来没有用过它。它在文档中定义为,

“char”类型(注意引号)与 char(1) 的不同之处在于它只使用一个字节的存储空间。它在系统目录中作为一种简单的枚举类型在内部使用。

并进一步,

"char"  1 byte  single-byte internal type
Run Code Online (Sandbox Code Playgroud)

那么,如果它是一个字节,那么域是什么,您将如何使用它?它是签名的还是未签名的?在@Erwin Brandstetter 的这篇文章中,他对此进行了阐述,但我仍然感到困惑。他正在使用ascii()and chr(),并提供了这个

SELECT i
     , chr(i)::"char"        AS i_encoded
     , ascii(chr(i)::"char") AS i_decoded
FROM   generate_series(1,256) i;
Run Code Online (Sandbox Code Playgroud)

这在 10 点到 11 点之间做一些非常奇怪的事情。

  i  | i_encoded | i_decoded 
-----+-----------+-----------
...
   8 | \x08      |         8
   9 |           |         9
  10 |          +|        10
     |           |           -- WTF is going on here.
  11 | \x0B      |        11
  12 | \x0C      |        12
...
Run Code Online (Sandbox Code Playgroud)

这里也变得非常奇怪:

 126 | ~         |       126
 127 | \x7F      |       127
 128 |           |       128
 129 |           |       128
 130 |           |       128
 131 |           |       128
Run Code Online (Sandbox Code Playgroud)

为什么 128 以北的所有内容都被解码为 128?但是为了让大家感到奇怪,在 192 之后有一个开关,它们被解码为 192 ..

 190 |           |       128
 191 |           |       128
 192 |           |       192
 193 |           |       192
 194 |           |       192
 195 |           |       192
 196 |           |       192
 197 |           |       192
Run Code Online (Sandbox Code Playgroud)

欧文 说

有几个字符不适合显示。因此,在存储之前进行编码并在显示之前进行解码......

我不知道为什么我们应该编码,尽管如果我们完全按照问题的要求去做

CREATE TABLE foo AS
SELECT i::"char"
FROM   generate_series(-128,127) i;
Run Code Online (Sandbox Code Playgroud)

这很好用。我们可以使用

SELECT i::int FROM foo;
Run Code Online (Sandbox Code Playgroud)

所以简而言之,

  1. Erwin 的代码在 10 到 11 之间做什么,其中 i 变为空?
  2. 为什么128重复了这么多次?
  3. 为什么192重复了这么多次?
  4. 当 Erwin 说您不能以这种方式编码 0时,我如何触发无法存储 0 (不允许使用空字符)

    CREATE TABLE foo AS SELECT 0::int::"char" AS x;
    SELECT x::int FROM foo;
     x 
    ---
    0
    
    Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 11

1. chr(10)

... 产生LINEFEED字符(又名转义序列\n)并且 psql 显示带有换行符的字符(由 表示+)。那里一切都正确。

2. & 3.ascii()产生 128 或 192?

它始于我犯的一个错误。我粗心地认为"char"会涵盖一个范围引用的答案(现已修复)中无符号1 字节整数(0 到 255)的范围,但它实际上是内部有符号1 字节整数(-128 到 127)的范围。

ascii()接受一个text参数,隐式转换来自"char"to转换text产生一个多字节编码的 unicode 字符,并且该函数返回(根据 上的文档ascii()):

参数第一个字符的 ASCII 码。对于 UTF8,返回字符的 Unicode 代码点。对于其他多字节编码,参数必须是 ASCII 字符。

所以我们得到了很多截断的值。128 和 192 是多字节字符的前导字节的字节值。

4. 空字节

无法存储空字节仅影响常规字符类型(text, char, varchar),而不影响"char". 它适用于我的越野车示例,因为我将其text作为垫脚石。在之间"char"integer直接进行转换时,限制不适用。手册chr()

不允许使用 NULL (0) 字符,因为文本数据类型无法存储此类字节。

“char”不是这样,其中0映射到空字符串''

SELECT ''::"char"::int  -- 0
     , 0::"char" = '';  -- t
Run Code Online (Sandbox Code Playgroud)

请记住:"char"仍然是一个“内部”类型,用于简单和廉价的枚举。不是为我们在这里所做的事情而正式设计的,并且不能移植到其他 RDBMS。Postgres 项目对此没有任何保证。

  • @Evan 不,它不会“跳过一行”,空行是前一行(多行)的延续。如果你可以让 psql 在输出行之间绘制水平线,这会更明显,但因为你不能视觉线索是“+”。 (4认同)