Postgresql使用函数检查数组中所有单个元素的约束

时间:2017-07-28 14:43:34

标签: arrays postgresql

如果我有桌子

create table foo ( bar text[] not null check ..... );

和一个功能

create function baz(text) returns boolean as $$ .....

如何在foo表中添加检查约束,以便bar字段中的每个元素都验证baz函数?

我在想我需要创建一个函数

create function array_baz(arg text[]) returns boolean as $$ 
    with x as ( select baz(unnest(arg)) as s_arg )
    select not exists (select 1 from x where s_arg = false)
$$ language sql strict immutable;

 create table foo (bar text[] not null check ( array_baz(bar) = true ) );

然而,我确信我在这里重新发明轮子,这是一种可行的方法。我错过了什么psql技巧?地图功能很不错

create table foo (bar text[] not null check (true = all(map('baz', bar)));

但到目前为止,我的搜索工作毫无结果。

1 个答案:

答案 0 :(得分:2)

你可以通过多种方式做你想做的事。如果要使用ALL(...)量词,则需要合适的运算符。为此,您首先需要一个功能来执行您想要的任务:

想象一下,您想要检查您的文本中是否包含任何大写字母。你定义了一个像:

这样的函数
CREATE FUNCTION doesnt_have_uppercase(b boolean, t text)
/* Compares b to the result of `t` not having any non-lowercase character */
    RETURNS boolean
    IMMUTABLE
    STRICT
    LANGUAGE SQL
AS
$$
    SELECT (t = lower(t)) = b
$$ ;

基于此,create an operator

CREATE OPERATOR =%= (
    PROCEDURE = doesnt_have_uppercase,
    LEFTARG = boolean,
    RIGHTARG = text
) ;

您需要此运算符,因为ANY and ALL quantifiers需要以下结构:

expression operator ALL(array)

此时,您可以定义:

create table foo 
( 
    bar text[] not null,
    CONSTRAINT bar_texts_cant_have_uppercase CHECK(true =%= ALL(bar))
);

这将引导您出现以下行为:

INSERT INTO foo
    (bar)
VALUES
    (ARRAY['this will pass', 'this too']) ;
1 rows affected
INSERT INTO foo
    (bar)
VALUES
    (ARRAY['that would pass', 'BUT THIS WILL PREVENT IT']) ;
ERROR:  new row for relation "foo" violates check constraint "bar_texts_cant_have_uppercase"
DETAIL:  Failing row contains ({"that would pass","BUT THIS WILL PREVENT IT"}).

全部检查 dbfiddle here

但是,我很可能会寻求一条不那么曲折的路线:

CREATE FUNCTION doesnt_have_uppercase(t text[])
/* Returns true if all elements of t don't have any uppercase letter */
    RETURNS boolean
    IMMUTABLE
    STRICT
    LANGUAGE SQL
AS
$$
    SELECT (NOT EXISTS (SELECT 1 FROM unnest(t) q WHERE q <> lower(q)))
$$ ;

create table foo 
( 
    bar text[] not null,
    CONSTRAINT bar_texts_cant_have_uppercase CHECK(doesnt_have_uppercase(bar))
);

这与上一个示例完全相同(除非数组的某些元素为NULL)。

dbfiddle here