插入整数列时不要默默地舍入浮动输入 解决方法?解决方案?

问题描述

我有一个像这样的表

CREATE TABLE foo(bar int)

我有一个脚本,可以将值插入到该表中

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

float值被自动舍入以适合数据类型:

> SELECT * FROM foo;
 bar
-----
   1
   2
   3
(3 rows)

Postgres内置了什么可以防止这种情况发生,而是引发错误吗? (甚至是警告?)

解决方法

数字常量3.2最初解析为数据类型numeric(不是float)。手册here中的详细信息。

integer列的分配将以静默方式工作,因为在标准Postgres中为numeric-> integer注册了一个“分配”强制转换。

要获得所需的行为,您将不得不在实现Postgres源代码中的强制转换的函数中引入警告(并重新编译)。要付出很高的代价。 (版本更新如何?)

或者您删除或更改注册的演员表。您可以使用基于您自己的功能的版本替换强制转换-并在其中引发WARNING。但这很昂贵,并且可能会向很多警告发送垃圾邮件。

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

解决方法?

可以使用这种简单的解决方法:仅对交易禁用转换:

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;

现在,如果实际输入numeric,则会引发异常。 但是,您需要超级用户权限才能执行此操作。您可以将其封装在SECURITY DEFINER函数中。相关:

并且您不想长时间锁定系统目录pg_cast,因为并发操作可能会发生。因此,我宁愿不使用并发性。

解决方案?

您可以将输入值移至CTE并在WHERE子句中进行测试,以在所有插入值都不都是有效整数值的情况下(无提示)跳过插入内容:

WITH input(i) AS (
   VALUES (1),(3.2)  -- one numeric makes all fall back to numeric
   )
INSERT INTO foo (bar)
SELECT i
FROM   input
WHERE  (SELECT pg_typeof(i) FROM input LIMIT 1) = 'integer'::regtype;

db 提琴here

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

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