插入整数列时不要静默舍入浮点输入

Lon*_*Rob 6 postgresql postgresql-12

我有一张像:

CREATE TABLE foo(bar int)
Run Code Online (Sandbox Code Playgroud)

我有一个将值插入该表的脚本:

INSERT INTO foo(bar)
VALUES (1), (2), (3.2)
Run Code Online (Sandbox Code Playgroud)

浮点值被静默四舍五入以适合数据类型:

> SELECT * FROM foo;
 bar
-----
   1
   2
   3
(3 rows)
Run Code Online (Sandbox Code Playgroud)

Postgres 是否有任何内置功能可以防止这种情况发生,而是引发错误?(甚至是警告?)

Erw*_*ter 3

数值常量3.2最初解析为数据类型numeric(不是float)。详细信息请参阅此处的手册。

对列的赋值会默默地进行,因为在标准 Postgres 中为-->integer注册了一个“赋值”转换。numericinteger

为了获得所需的行为,您必须在 Postgres 源代码中实现强制转换的函数中添加警告(并重新编译)。付出的代价非常高。(版本更新怎么样?)

或者您删除或更改已注册的演员。您可以用基于您自己的功能的版本替换演员 - 并WARNING在那里提出 a 。但这是昂贵的,并且可能会发出大量警告。

您不想完全删除该演员表。很多很多的计算都使用它。

解决方法?

可以使用这个简单的解决方法:仅禁用事务的转换:

BEGIN;

UPDATE pg_cast
SET    castcontext = 'e'               -- make the cast "explicit"
WHERE  castsource = 'numeric'::regtype
AND    casttarget = 'integer'::regtype;

INSERT INTO foo(bar)
VALUES (1), (2), (3.2);

UPDATE pg_cast
SET    castcontext = 'a'               -- revert back to "assignment"!
WHERE  castsource = 'numeric'::regtype
AND    casttarget = 'integer'::regtype;

COMMIT;
Run Code Online (Sandbox Code Playgroud)

现在,在实际输入的情况下会引发异常numeric您需要超级用户权限才能执行此操作。您可以将其封装在一个SECURITY DEFINER函数中。有关的:

并且您不希望pg_cast在可能发生并发操作时长时间锁定系统目录。所以我不想在并发的情况下这样做。

解决方案?

您可以将输入值移动到 CTE 并在子句中进行测试WHERE,以跳过插入(静默)(如果它们不都是有效的整数值):

BEGIN;

UPDATE pg_cast
SET    castcontext = 'e'               -- make the cast "explicit"
WHERE  castsource = 'numeric'::regtype
AND    casttarget = 'integer'::regtype;

INSERT INTO foo(bar)
VALUES (1), (2), (3.2);

UPDATE pg_cast
SET    castcontext = 'a'               -- revert back to "assignment"!
WHERE  castsource = 'numeric'::regtype
AND    casttarget = 'integer'::regtype;

COMMIT;
Run Code Online (Sandbox Code Playgroud)

db<>在这里摆弄

然后您可以检查命令标签是否插入了任何内容。

或者将其全部包装在 plpgsql 函数中,检查是否实际插入了任何内容,RAISE如果没有则检查您需要的内容。相关例子: