如何让自定义Postgres now()函数优先级高于pg_catalog中的同名函数?
如何让自定义now()函数优先级高于Postgres内置函数?
在测试数据库中,我尝试通过创建自定义函数覆盖Postgres的now()以实现时间调整,代码如下:
CREATE SCHEMA if not exists override; CREATE OR REPLACE FUNCTION override.now() RETURNS timestamp with time zone AS $$ BEGIN RETURN pg_catalog.now() + COALESCE( NULLIF(current_setting('timecop.offset_in_seconds', true), '')::integer, 0 ) * interval '1 second'; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE STRICT; SET search_path TO DEFAULT; SELECT set_config('search_path', 'override,' || current_setting('search_path'), false);
通过SET timecop.offset_in_seconds = 3600启用时间偏移后,调用now()却未生效,查询函数列表发现pg_catalog中的now()优先级更高:
app_test=# \df+ now List of functions Schema | Name | Result data type | Argument data types | Type | Volatility | Parallel | Owner | Security | Access privileges | Language | Source code | Description ------------+------+--------------------------+---------------------+------+------------+----------+----------+----------+-------------------+----------+-------------+-------------------------- pg_catalog | now | timestamp with time zone | | func | stable | safe | postgres | invoker | | internal | now | current transaction time
问题原因
Postgres默认会搜索pg_catalog中的内置函数,但如果自定义schema位于search_path的最前端,同签名的自定义函数会被优先调用。你的情况大概率是search_path设置未正确生效,导致Postgres仍优先使用系统内置函数。
解决方法
1. 确认自定义函数与schema存在
先执行以下SQL确认override.now()已正确创建:
SELECT proname, nspname FROM pg_proc JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid WHERE proname = 'now';
正常应返回两条记录:(now, override)和(now, pg_catalog)。
2. 确保search_path将override置于最前端
Postgres会按search_path的顺序查找函数,将自定义schema放在最前面即可优先匹配:
- 临时生效(当前会话):
设置后直接测试SET search_path = override, "$user", public;SELECT now();即可生效。 - 永久生效(针对数据库):
执行后需要重新连接会话才会生效。ALTER DATABASE app_test SET search_path = override, "$user", public; - 永久生效(针对特定用户):
同样需要重新连接会话。ALTER ROLE your_test_user SET search_path = override, "$user", public;
3. 极端方案(测试环境慎用)
如果上述方法无效,且你拥有超级用户权限,可以直接替换pg_catalog中的now()函数(仅推荐测试环境临时使用):
CREATE OR REPLACE FUNCTION pg_catalog.now() RETURNS timestamp with time zone AS $$ BEGIN RETURN pg_catalog.current_timestamp + COALESCE( NULLIF(current_setting('timecop.offset_in_seconds', true), '')::integer, 0 ) * interval '1 second'; END; $$ LANGUAGE plpgsql STABLE PARALLEL SAFE STRICT;
测试完成后记得恢复系统函数:
CREATE OR REPLACE FUNCTION pg_catalog.now() RETURNS timestamp with time zone AS 'SELECT current_timestamp' LANGUAGE sql STABLE PARALLEL SAFE STRICT;
内容的提问来源于stack exchange,提问作者23tux
相关产品推荐
相关产品推荐

