当我更改基础函数时,在函数上构建的索引会发生什么?
说,我有一个函数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]
?
(有问题的功能是相似的,而是使用相当冗长的一系列替换,这些替换仍在调整但预计会保持稳定。)
答案 0 :(得分:6)
IMMUTABLE
表示“不会改变”或“不可改变”。严格避免违反该规则必须做的是删除该功能和依赖于它的所有内容然后重新创建它以及使用它的索引。
如果您就地更换功能,则需要对后果负责。我个人认为,由于这个原因,PostgreSQL应该禁止OR REPLACE
IMMUTABLE
函数,这会迫使你像设置ignore_immutable_checks_even_though_it_might_cause_incorrect_queries
配置选项一样跳过额外的环节。
如果更改不可变函数的行为,则基于该函数的索引无效。服务器无法判断函数的行为是否已更改;您可能只是将其替换为在各方面具有相同行为的优化版本。所以它不会使你的索引无效,但也许它应该是,因为如果你的函数的行为确实不同,你可以根据函数得到不正确的查询结果。
答案 1 :(得分: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)
答案 2 :(得分: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".