8.4. 二进制数据类型

bytea数据类型允许存储二进制字符串。参阅表 8-6

表 8-6. 二进制数据类型

名字存储空间描述
bytea1或4字节加上实际的二进制字符串变长的二进制字符串

二进制字符串是一个字节序列。二进制字符串和普通字符字符串的区别有两个: 首先,二进制字符串完全可以存储字节零值以及其它"不可打印的" 字节(定义在 32 到 126 范围之外的字节)。字符串不允许字节零值, 并且也不允许那些不符合选定的字符集编码的非法字节值或者字节序列。 第二,对二进制字符串的处理实际上就是处理字节,而对字符串的处理则取决于区域设置。 简单说,二进制字符串适用于存储那些程序员认为是"原始字节"的数据, 而字符串适合存储文本。

bytea类型支持两种输入输出的外部格式:"hex" 格式和PostgreSQL的历史"escape"格式。 这两种格式通常在输入中使用,输出格式由bytea_output 配置参数决定,默认是hex。(需要注意的是hex格式是在PostgreSQL 9.0中引入的,早期版本和一些工具中不识别。)

SQL标准定义了一个不同的二进制字符串格式,称为BLOBBINARY LARGE OBJECT。输入格式与bytea不同, 但提供的函数和操作符基本一致。

8.4.1. bytea十六进制格式

"hex"格式将二进制数据编码为每字节2位十六进制数字, 最重要的四位(半字节)放在开始。整条字符串以\x开始 (与转义格式相区别)。在某些情况下,最初的反斜杠需要再写一次,以转义, 同样,在转义格式中,反斜杠需要写两个,下面会详细介绍。 十六进制数字可以大写也可以小写,并且数字对之间允许有空格(但不能是在一个数对, 也不能是在\x起始序列)。 十六进制格式能够与许多的外部应用程序和协议兼容,并且转换往往比转义快, 因此更倾向于这种用法。

例子:

SELECT E'\\xDEADBEEF';

8.4.2. bytea转义格式

对于bytea格式来说,"escape"格式是一种传统的 PostgreSQL格式。它采用以ASCII字符序列来表示二进制串的方法, 同时将那些无法表示成ASCII字符的二进制串转换成特殊的转义序列。从应用的角度看, 如果代表字节的字符有意义,那么这种表示方法会很方便。但实际上, 这样做会模糊二进制字符串和字符字符串之间的区别,从而造成困扰, 同时筛选出的转义机制会显得很臃肿。因此对一些新应用应该恰当的避免这种格式。

当以转义格式录入bytea值时,你必须转义某些字节值, 同时可以转义所有字节值。通常,要转义一个字节值, 将它转换成反斜杠+三位八进制值的形式(或两个反斜杠,如果用转义字符串语法将值写成文本形式)。 另外,反斜杠本身(字节值92)可以用双反斜杠表示。表 8-7 给出了必须转义的字符串,和替代的转义序列(如适用)。

表 8-7. bytea文本转义八进制

十进制数值描述输入转义形式例子输出形式
0八进制的零E'\\000'SELECT E'\\000'::bytea;\000
39单引号''''E'\\047'SELECT E'\''::bytea;'
92反斜杠E'\\\\'E'\\134'SELECT E'\\\\'::bytea;\\
0 to 31 and 127 to 255"不可打印"八进制字符E'\\xxx' (八进制值)SELECT E'\\001'::bytea;\001

转义不可打印字节的要求因区域设置而异。在某些场合下, 你可以不转义它们。请注意表 8-7 里的每个例子都是刚好一个字节长,虽然输出形式比一个字符要长。

你必须写这么多反斜杠的原因,如表 8-7所示, 是因为一个写成字符串文本的输入字符串必须通过PostgreSQL 服务器里的两个分析阶段。每一对反斜杠中的第一个会被字符串文本分析器理解成一个转义字符而消耗掉, 于是剩下的第二个反斜杠被bytea输入函数当作一个三位八进制值或者是转义另外一个反斜杠的开始。 比如,一个传递给服务器的字符串文本E'\\001'在通过字符串分析器之后会当作\001 发送给bytea输入函数,在这里它被转换成一个十进制值为 1 的单个字节。请注意, 单引号字符(')不会被bytea特殊对待,它遵循字符串文本的普通规则。又见第 4.1.2.1 节

Bytea字节也在输出中转义。通常,每个"不可打印" 的字节值都转化成对应的前导反斜杠的三位八进制数值。大多数"可打印的" 字节值是以客户端字符集的标准表现形式出现的。十进制值为 92(反斜杠)的字节在输出中双写。 细节在表 8-8里描述。

表 8-8. bytea输出转义序列

字节的十进制值描述转义的输出形式例子输出结果
92反斜杠\\SELECT E'\\134'::bytea;\\
0 to 31 and 127 to 255"不可打印"八进制字符\xxx (八进制值)SELECT E'\\001'::bytea;\001
32 to 126"可打印"八进制字符客户端字符集表现形式SELECT E'\\176'::bytea;~

根据你使用的前端不同,在是否转义bytea字符串的问题上你可能有一些额外的工作要做。 比如,如果你的接口自动转换换行和回车,那你可能还要转义它们。