8

当我更改底层函数时,基于函数构建的索引会发生什么情况?

说,我有一个函数clean_name()定义为:

CREATE OR REPLACE FUNCTION clean_name(n text)
RETURNS TEXT AS
$BODY$
DECLARE
 rec TEXT;
BEGIN
 EXECUTE
  'SELECT Regexp_replace(' || quote_literal(n) || ', ''[a-z]'', '''', ''g'');'
 INTO rec;
RETURN rec;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE
;

然后创建一个索引:

CREATE INDEX my_table_upper_name_btree
ON schema.my_table USING GIST (my_text_field);

但后来我决定重新定义删除大写字母的函数。我创建的索引会发生什么?它会自行改变吗?我一次DROP又一次CREATE吗?VACUUM [ANALYZE] [FULL]?

(有问题的功能是相似的,而是使用了一系列相当长的替代品,这些替代品仍在调整中,但预计会稳定。)

4

3 回答 3

6

IMMUTABLE意思是“不变”或“不变”。为了严格避免违反该规则,您必须做的是删除函数和依赖它的所有内容,然后重新创建它和使用它的索引。

如果您就地更换功能,您将对后果负责。就我个人而言,我认为 PostgreSQL 应该因为这个原因而不允许OR REPLACE使用IMMUTABLE函数,迫使你跳过一个额外的环节,比如设置ignore_immutable_checks_even_though_it_might_cause_incorrect_queries配置选项。

如果您更改不可变函数的行为,则基于该函数的索引无效。服务器无法判断函数的行为是否发生了变化;您可能只是将其替换为在各个方面都具有相同行为的优化版本。因此,它不会使您的索引无效,尽管它可能应该这样做,因为如果您的函数的行为确实不同,您可能会根据该函数获得不正确的查询结果。

于 2013-07-12T08:15:17.657 回答
4

如果更改函数,则必须重建索引。

create table t (i integer);
insert into t (i)
select generate_series(1, 100000);
analyze t;

返回相反整数的简单函数:

create or replace function f(i integer)
returns integer as $$
select i * -1;
$$ immutable language sql;

以及上面的索引:

create index t_i_index on t(f(i));

使用索引:

explain select * from t order by f(i);
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Index Scan using t_i_index on t  (cost=0.00..3300.26 rows=100000 width=4)

现在函数更改为返回整数本身:

create or replace function f(i integer)
returns integer as $$
select i;
$$ immutable language sql;

并且不再使用索引:

explain select * from t order by f(i);
                          QUERY PLAN                           
---------------------------------------------------------------
 Sort  (cost=11116.32..11366.32 rows=100000 width=4)
   Sort Key: i
   ->  Seq Scan on t  (cost=0.00..1443.00 rows=100000 width=4)

如果重建索引

reindex index t_i_index;

再次使用它:

explain select * from t order by f(i);
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Index Scan using t_i_index on t  (cost=0.00..4376.26 rows=100000 width=4)
于 2013-07-11T20:12:20.407 回答
0

我惊讶地发现 Postgres 如此依赖管理员的良好行为来防止对索引使用(“曾经”)的函数发生更改。我实际上认为这是非常有问题的。没有什么可以阻止错误的行为,我认为这是一个设计问题,而不是用户错误。

所以我决定制作一个方便的小系统来使用事件触发器(以及一点点正则表达式黑客)来执行这种强制。我认为它的保水性很好,但我还没有以任何高压方式对其进行测试。

使用这些:

CREATE OR REPLACE FUNCTION pg_index_monitor()
        RETURNS event_trigger LANGUAGE plpgsql AS $$
DECLARE
    obj record;
BEGIN
    FOR obj IN (
        WITH
            index_functions AS
        (
        select
            unnest(regexp_matches(indexprs, '(?<=(funcid(\s)))(\d+)', 'g'))::oid as objid
        ,   indexrelid
        FROM pg_index
        WHERE
            indexprs IS NOT NULL
        )
        SELECT
            *
        FROM pg_event_trigger_ddl_commands()
        JOIN index_functions USING (objid)
        JOIN pg_class ON indexrelid = oid
        where
            object_type = 'function'
    )
    LOOP
        RAISE EXCEPTION 'This function cannot be modified as it is being used by index "%".', obj.relname;
    END LOOP;
END
$$
;

DROP EVENT TRIGGER IF EXISTS pg_index_monitor_trigger;
CREATE EVENT TRIGGER pg_index_monitor_trigger
   ON ddl_command_end
EXECUTE FUNCTION pg_index_monitor();

然后,如果我有一个依赖于正在更新的函数的索引(旧的或新的),它将引发错误。

CREATE INDEX some_index ON some_table (test(some_column));

然后尝试编辑该链接功能:

CREATE OR REPLACE FUNCTION test(text) RETURNS text AS $$
select 'abc'::text;
$$
LANGUAGE sql
IMMUTABLE
;

[P0001] ERROR: This function cannot be modified as it is being used by index "some_index".

于 2020-10-07T07:26:51.573 回答