插入触发器中的字符串匹配 - 如何在条件中使用返回NULL?

时间:2013-03-19 13:01:17

标签: postgresql triggers plpgsql string-matching postgresql-8.4

在使用UTF8编码的PostgreSQL 8.4.13数据库中,我在此表中保留了一个(非英语)字典:

create table good_words (
        word varchar(64) primary key
);

以及:

中的错误列表(但通常是建议的或错误的)
create table bad_words (
        word varchar(64) primary key
);

对于前一个表格,我有一个BEFORE INSERT trigger,我试图扩展它:

create or replace function keep_clean() returns trigger as $body$
        begin
                new.word := upper(new.word);

                perform true
                        from bad_words
                        where word = new.word;

                if found then
                        return null;
                end if;

                -- forbid words with [XYZ] at beginning and Z at the end
                -- forbid words with LLL unless it is KLLL or MLLL

                return new;
        end;
$body$ language plpgsql;

create trigger count_letters
        BEFORE INSERT on good_words
        for each row execute procedure keep_clean();

我的问题是:我正在尝试向触发器添加3条规则(返回NULL):

  1. 禁止以X,Y或Z字母开头的字样
  2. 以Z字母结尾的单词被禁止
  3. 连续3个相同字母的单词非常罕见,只有在like '%KLLL%' or like '%MLLL%'
  4. 时才允许

    作为Perl程序员,我知道正则表达式,但我的问题是Pl / PgSQL部分 - 如何使用该语言执行正则表达式匹配,我是否总是必须使用SELECT INTOPERFORM?或者我可以在这里使用:=运算符,甚至可以在IF语句中执行字符串匹配吗?

    更新

    克雷格的解释之后(谢谢!)我准备了the following SQL Fiddle,但它仍有2个问题:

    create table good_words (
            word varchar(64) primary key
    );
    
    create or replace function keep_clean() returns trigger as $body$
            begin
                    new.word := upper(new.word);
    
                    /* next line does not compile? */
                    IF new.word !~ '^[\x0410-\x042F]{2,}$' THEN
                        RAISE EXCEPTION 'Not an uppercased Russian word in UTF8';
                    END IF;
    
                    IF new.word ~ '^[ЪЫЬ]' OR new.word ~ 'Ъ$' THEN
                        return NULL;
                    END IF;
    
                    /* does not return NULL for 'ошибббка'? */
                    IF new.word ~ '(.)\1\1' AND new.word NOT LIKE '%ШЕЕЕ%' AND new.word NOT LIKE '%ЗМЕЕЕ%' THEN
                        return NULL;
                    END IF;
    
                    return new;
            end;
    $body$ language plpgsql;
    

    这里不应该采用UTF8编码的前两个单词,但确实如此:

    insert into good_words (word)
      values
        ('abcde'),          /* bad word: non-russian */
        ('ошибббка'),       /* bad word: 3 letters in a row */
        ('длинношеее'),
        ('проверка')
    ;
    
    select * from good_words;
    

    更新2:触发功能现在正常运行,谢谢:http://sqlfiddle.com/#!11/98403/1

1 个答案:

答案 0 :(得分:2)

使用PostgreSQL的~运算符或regexp_matches函数。请参阅文档中的pattern matching

简单的PL / PgSQL示例:

DO
$$
BEGIN
  IF 'XABCK' ~ '^[XY]' THEN
    RAISE EXCEPTION 'Disallowed character';
  END IF;
END;
$$;

如您所见,IF可以表达。这些表达式可能具有任意复杂性,可能包含子查询CASE,几乎所有SQL中都是合法的。