插入整数列时不要静默舍入浮点输入 [英] Don't round float input silently when inserting into integer column

查看:43
本文介绍了插入整数列时不要静默舍入浮点输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张像:

CREATE TABLE foo(bar int)

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

I have a script which inserts values into that table:

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

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

The float value is silently rounded to fit the data type:

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

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

Is there anything built-in to Postgres which can prevent this from happening and, instead, raise an error? (Or even a warning?)

推荐答案

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

The numeric constant 3.2 initially resolves to data type numeric (not float). Details in the manual here.

integer 列的赋值静默工作,因为存在一个赋值"为 numeric 注册的演员表 -->标准 Postgres 中的 integer.

The assignment to an integer columns works silently because there is an "assignment" cast registered for numeric --> integer in standard Postgres.

要获得所需的行为,您必须在 Postgres 源代码中实现强制转换的函数中添加警告(并重新编译).一个非常高的代价.(版本更新呢?)

To get the desired behavior, you would have to hack a warning into the function that implements the cast in the Postgres source code (and recompile). A very high price to pay. (What about version updates?)

或者您删除或更改已注册的演员表.您可以使用基于您自己的函数的版本替换演员表 - 并在其中引发 WARNING .但这很昂贵,并且可能会发送很多警告.

Or you remove or change the registered cast. You could replace the cast with a version based on your own function - and raise a WARNING in there. But that's expensive, and may spam a lot of warnings.

您不想完全删除该演员表.大量计算都使用它.

You don't want to remove that cast completely. Lots and lots of calculations use it.

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

You could use this simplistic workaround: disable the cast for the transaction only:

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 函数中.相关:

Now, an exception is raised in case of actual numeric input. But you need superuser privileges to do that. You might encapsulate it in a SECURITY DEFINER function. Related:

并且您不想长时间锁定系统目录 pg_cast 而并发操作可能发生.所以我宁愿不要用并发来做这件事.

And you don't want to lock the system catalog pg_cast for long while concurrent operations can happen. So I'd rather not do that with concurrency around.

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

You could move input values to a CTE and test in a WHERE clause to skip inserts (silently) if they aren't all valid integer values:

WITH input(i) AS (
   VALUES (1), (2), (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<>fiddle 这里

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

You could then check the command tag whether anything was inserted.

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

Or wrap it all in a plpgsql function, check whether anything was actually inserted, and RAISE whatever you need if not. Related examples:

这篇关于插入整数列时不要静默舍入浮点输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆