【问题标题】:Boolean value gets inserted into character varying field in PostgreSQL布尔值被插入到 PostgreSQL 中的字符变化字段中
【发布时间】:2015-01-30 00:17:35
【问题描述】:

PostgreSQL 9.1 很乐意将布尔值插入到 varchar 字段中。我的期望是这会失败,因为我插入了错误类型的数据,我希望这样可以在我的应用程序逻辑中指出错误。除了为每个 varchar 字段创建触发器或约束之外,还有什么方法可以使其成为默认行为?

CREATE TABLE
foo
(
    text_field CHARACTER VARYING(32)
);

这两个都在 text_field 中插入“false”:

INSERT INTO foo (text_field) VALUES (FALSE);
INSERT INTO foo (text_field) VALUES (CAST('f' AS BOOLEAN));

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    发生这种情况是因为 PostgreSQL 将接受任何类型的表达式(在 INSERT 语句中),其类型可以自动转换为该列的类型(更准确地说,这两种类型之间存在 ASSIGNMENT or IMPLICIT 转换:这就是为什么这些演员表首先存在)。前:

    -- lets see what casts exists to boolean & text
    -- "castcontext" is 'a' for ASSIGNMENT, 'i' for IMPLICIT and 'e' for EXPLICIT casts
    select castsource::regtype::text,
           casttarget::regtype::text,
           castcontext
    from   pg_cast
    where  casttarget in ('boolean'::regtype, 'text'::regtype);
    
    create table foo (
      varchar_field varchar,
      text_field    text,
      bool_field    boolean
    );
    
    -- these are equivalents, because of some casts
    insert into foo values ('false', 'false', 'false');
    insert into foo values (false, false, false);
    -- this will throw an error
    insert into foo values (0, 0, 0);
    

    好吧,我作弊了一点。有一种特殊情况。当你写一个 string 文字(如'false')时,它有一个unknown 类型。未知值总是传递给实际列类型的input function。真正的text 类型文字看起来有点不同:text 'false'。如果再次查看 PostgreSQL 提供的默认转换,您会发现从 text 到 boolean 没有转换。这就是最后一条语句失败的原因:

    insert into foo values (text 'false', text 'false', text 'false');
    

    SQLFiddle

    编辑:所以,默认情况下,有一个从boolean 到text 的ASSIGNMENT 转换(character varying 也有一个)。这就是为什么您的表的 text_field 接受布尔输入。

    【讨论】:

      【解决方案2】:

      我的期望是这会失败,因为我插入了错误类型的数据。 . .

      SQL 语句通常涉及混合数据类型。 SQL 引擎(不仅仅是 PostgreSQL)遵循将一种类型的值静默转换为另一种类型的值的内部规则。 PostgreSQL 的规则记录在Type Conversion 中。插入值遵循的规则在Value Storage 中。

      虽然可能可以更改 pg_cast 系统目录的内容(我不知道),但我认为这是一个真的坏主意。一方面,pg_cast 不支持 pg_dump。灾难恢复可能很容易将 dbms 置于您不期望的状态,并且很难进行故障排除。

      我认为最好的办法是停止将布尔值插入到它们不应该出现的列中。说真的,为什么应用程序代码正在执行诸如将“城市”之类的列设置为 False 之类的事情?

      您的下一个最佳选择是添加 CHECK 约束。

      alter table foo
      add constraint disallow_tf_text_field
      check (lower(text_field) not in ('t', 'f', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off', '1', '0'));
      

      【讨论】:

      • 感谢您的回答。代码的目的是在字段中存储一个字符串。 PHP 执行 $text = substr($text, 0, 32);。这在大多数情况下都按预期工作,但如果 $text 的长度为 0,则返回 false 而不是 ""。可能从 PHP 切换会有所帮助。 :)
      • 好吧,任何接受到 varchar(n) 列中的东西都会变成一个字符串。但是 PHP 的错误处理在程序员中是臭名昭著的。例如,strpos() 可以返回布尔值 false,但它也可以返回 评估 为 false 的非布尔值。你必须非常小心 PHP 的返回值。
      猜你喜欢
      • 2011-08-28
      • 2018-03-27
      • 2011-08-06
      • 2019-01-17
      • 1970-01-01
      • 2011-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多