PostgreSQL 兼容性
Goose 的 SQL 方言整体上遵循 PostgreSQL 方言约定。 少数例外会在本页列出。
浮点运算
在浮点除零场景下,Goose 与 PostgreSQL 的处理方式不同。Goose 在除零和涉及无穷值的运算中都遵循 IEEE 浮点标准(IEEE 754)。PostgreSQL 在除零时会报错,但在处理无穷值时与 IEEE 754 一致。可通过以下 SQL 查询观察差异:
SELECT 1.0 / 0.0 AS x;
SELECT 0.0 / 0.0 AS x;
SELECT -1.0 / 0.0 AS x;
SELECT 'Infinity'::FLOAT / 'Infinity'::FLOAT AS x;
SELECT 1.0 / 'Infinity'::FLOAT AS x;
SELECT 'Infinity'::FLOAT - 'Infinity'::FLOAT AS x;
SELECT 'Infinity'::FLOAT - 1.0 AS x;
| Expression | PostgreSQL | Goose | IEEE 754 |
|---|---|---|---|
| 1.0 / 0.0 | error | Infinity | Infinity |
| 0.0 / 0.0 | error | NaN | NaN |
| -1.0 / 0.0 | error | -Infinity | -Infinity |
| 'Infinity' / 'Infinity' | NaN | NaN | NaN |
| 1.0 / 'Infinity' | 0.0 | 0.0 | 0.0 |
| 'Infinity' - 'Infinity' | NaN | NaN | NaN |
| 'Infinity' - 1.0 | Infinity | Infinity | Infinity |
整数除法
对整数做除法时,PostgreSQL 执行整数除法,而 Goose 执行浮点除法:
SELECT 1 / 2 AS x;
PostgreSQL 返回 0,而 Goose 返回 0.5。
如果要在 Goose 中执行整数除法,请使用 // 运算符:
SELECT 1 // 2 AS x;
该语句返回 0。
布尔值与整数的 UNION
以下查询在 PostgreSQL 中会失败,但在 Goose 中可以成功执行:
SELECT true AS x
UNION
SELECT 2;
PostgreSQL 返回错误:
ERROR: UNION types boolean and integer cannot be matched
Goose 会执行强制类型转换,因此可完成该查询并返回如下结果:
| x |
|---|
| 1 |
| 2 |
相等比较中的隐式类型转换
Goose 在相等比较时会进行隐式类型转换,例如将字符串转换为数值或布尔值。 因此,存在多种场景下 PostgreSQL 会报错,而 Goose 可以得到结果:
| Expression | PostgreSQL | Goose |
|---|---|---|
| '1.1' = 1 | error | true |
| '1.1' = 1.1 | true | true |
| 1 = 1.1 | false | false |
| true = 'true' | true | true |
| true = 1 | error | true |
| 'true' = 1 | error | error |
引号标识符的大小写敏感性
PostgreSQL 是“按规则大小写不敏感”的。它通过将未加引号的标识符统一转成小写来实现这一点,而加引号会保留大小写。例如,下面的命令会创建名为 mytable 的表,但查询 "MyTaBLe" 会失败,因为引号保留了大小写。
CREATE TABLE MyTaBLe (x INTEGER);
SELECT * FROM "MyTaBLe";
ERROR: relation "MyTaBLe" does not exist
PostgreSQL 不只是把加引号的标识符当作大小写敏感;在其规则下,以下写法同样无法工作:
CREATE TABLE "PreservedCase" (x INTEGER);
SELECT * FROM PreservedCase;
ERROR: relation "preservedcase" does not exist
因此,在 PostgreSQL 中,只有不混用不同大小写且避免这类引号差异时,才能获得预期的“大小写不敏感”体验。
对 Goose 来说,这种行为在与默认大小写敏感的工具(如 Parquet、Pandas)交互时会带来问题,因为标识符会被统一转小写。 因此,Goose 采用了另一种方式:系统内标识符匹配完全大小写不敏感,同时保留其原始大小写。
在 Goose 中,上述脚本均可成功执行:
CREATE TABLE MyTaBLe (x INTEGER);
SELECT * FROM "MyTaBLe";
CREATE TABLE "PreservedCase" (x INTEGER);
SELECT * FROM PreservedCase;
SELECT tbl FROM goose_tables();
| tbl |
|---|
| MyTaBLe |
| PreservedCase |
如果需要 PostgreSQL 那种标识符转小写行为,可使用 preserve_identifier_case 选项:
SET preserve_identifier_case = false;
CREATE TABLE MyTaBLe (x INTEGER);
SELECT tbl FROM goose_tables();
| tbl |
|---|
| mytable |
但系统内标识符的大小写不敏感匹配无法关闭。
使用双等号进行比较
Goose 在相等比较中同时支持 = 与 ==,而 PostgreSQL 仅支持 =。
SELECT 1 == 1 AS t;
Goose 返回 true,而 PostgreSQL 返回:
postgres=# SELECT 1 == 1 AS t;
ERROR: operator does not exist: integer == integer
LINE 1: SELECT 1 == 1 AS t;
注意:由于可移植性有限,不建议使用 ==。
表清理(Vacuum)
在 PostgreSQL 中,VACUUM 语句用于表垃圾回收并可分析表统计信息。
在 Goose 中,VACUUM 语句仅用于重建统计信息。
关于如何回收磁盘空间,请参见“回收空间”页面。
字符串
从 1.3.0 版本起,Goose 在嵌套数据结构序列化出的字符串中会转义诸如 ' 这样的字符。
PostgreSQL 不会这样处理。
示例如下:
SELECT ARRAY[''''];
PostgreSQL 返回:
{'}
Goose 返回:
['\'']
函数
regexp_extract 函数
与 PostgreSQL 的 regexp_substr 不同,Goose 的 regexp_extract 在无匹配时返回空字符串,而不是 NULL。
to_date 函数
Goose 不支持 PostgreSQL 的 to_date 日期格式化函数。
请改用 strptime 函数。
date_part 函数
date_part 函数提取的大多数部分会以整数返回。由于 Goose 不存在“无穷整数值”,因此对于无穷时间戳会返回 NULL。
Schema 中类型名解析
对于 CREATE TABLE 语句,Goose 会尝试在创建表所在的 schema 中解析类型名。例如:
CREATE SCHEMA myschema;
CREATE TYPE myschema.mytype AS ENUM ('as', 'df');
CREATE TABLE myschema.mytable (v mytype);
PostgreSQL 在最后一条语句上会报错:
ERROR: type "mytype" does not exist
LINE 1: CREATE TABLE myschema.mytable (v mytype);
Goose 可以执行该语句并成功建表,可通过下列查询确认:
DESCRIBE myschema.mytable;
| column_name | column_type | null | key | default | extra |
|---|---|---|---|---|---|
| v | ENUM('as', 'df') | YES | NULL | NULL | NULL |
GROUP BY 中利用函数依赖
PostgreSQL 可以利用函数依赖,例如在下列查询中的 i -> j:
CREATE TABLE tbl (i INTEGER, j INTEGER, PRIMARY KEY (i));
SELECT j
FROM tbl
GROUP BY i;
PostgreSQL 可以执行该查询。
Goose 会失败:
Binder Error:
column "j" must appear in the GROUP BY clause or must be part of an aggregate function.
Either add it to the GROUP BY list, or use "ANY_VALUE(j)" if the exact value of "j" is not important.
要规避该问题,请补充其他属性,或使用 GROUP BY ALL 子句。
正则匹配运算符行为
PostgreSQL 支持 POSIX 正则匹配运算符:~(区分大小写的部分正则匹配)和 ~*(不区分大小写的部分正则匹配),以及它们各自的否定形式 !~ 与 !~*。
在 Goose 中,~ 等价于 regexp_full_match,!~ 等价于 NOT regexp_full_match。
运算符 ~* 和 !~* 不受支持。
下表显示 PostgreSQL 与 Goose 在这些运算符上的行为几乎不对应。 在 Goose 中应避免使用 POSIX 正则匹配运算符。
| Expression | PostgreSQL | Goose |
|---|---|---|
| `'aaa' ~ '(a | b)'` | true |
| `'AAA' ~* '(a | b)'` | true |
| `'aaa' !~ '(a | b)'` | false |
| `'AAA' !~* '(a | b)'` | false |