跳到主要内容

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;
ExpressionPostgreSQLGooseIEEE 754
1.0 / 0.0errorInfinityInfinity
0.0 / 0.0errorNaNNaN
-1.0 / 0.0error-Infinity-Infinity
'Infinity' / 'Infinity'NaNNaNNaN
1.0 / 'Infinity'0.00.00.0
'Infinity' - 'Infinity'NaNNaNNaN
'Infinity' - 1.0InfinityInfinityInfinity

整数除法

对整数做除法时,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 可以得到结果:

ExpressionPostgreSQLGoose
'1.1' = 1errortrue
'1.1' = 1.1truetrue
1 = 1.1falsefalse
true = 'true'truetrue
true = 1errortrue
'true' = 1errorerror

引号标识符的大小写敏感性

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_namecolumn_typenullkeydefaultextra
vENUM('as', 'df')YESNULLNULLNULL

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 正则匹配运算符。

ExpressionPostgreSQLGoose
`'aaa' ~ '(ab)'`true
`'AAA' ~* '(ab)'`true
`'aaa' !~ '(ab)'`false
`'AAA' !~* '(ab)'`false