执行查询前校验用户:PostgreSQL中ALTER TYPE在DO块报错的解决办法咨询
解决方案:PostgreSQL中校验用户后执行ALTER TYPE ADD VALUE
这个问题我太熟悉了——PostgreSQL确实有个硬性限制:ALTER TYPE ... ADD VALUE这类枚举类型修改的DDL命令,不能在PL/pgSQL函数或者DO块这类服务器端的复合语句里执行,这是因为这类DDL会直接修改系统目录,PostgreSQL不允许在事务块或者函数上下文里做这类操作,所以你之前的DO块才会报错。
下面给你两种可行的实现方式,根据你的使用场景选择:
方案1:用psql客户端脚本做前置校验(推荐)
如果你的操作是通过psql客户端执行的,那直接用psql的内置控制命令做判断是最简单的,因为这些判断是在客户端执行的,不会把命令发送到服务器,完美避开限制:
-- 先在psql中校验当前用户 \if :{current_user = 'usrA'} ALTER TYPE enum_to_change ADD VALUE 'myNewValue'; \else \echo ERROR: 只有usrA用户才能执行此操作! \quit 1 -- 退出并返回错误码 \endif
执行这个脚本的时候,psql会先检查当前连接的用户,如果是usrA才会发送ALTER TYPE命令到服务器;否则直接在客户端报错退出,根本不会触发服务器端的限制。
方案2:用dblink在独立会话中执行DDL(服务器端方案)
如果必须在服务器端逻辑里完成校验和执行(比如不能依赖psql客户端),可以借助dblink扩展创建一个独立会话来执行DDL——因为dblink的会话是独立于当前PL/pgSQL上下文的,不受那个限制。
步骤1:安装dblink扩展
首先需要确保数据库里安装了dblink:
CREATE EXTENSION IF NOT EXISTS dblink;
步骤2:创建校验并执行的函数
CREATE OR REPLACE FUNCTION add_enum_value_for_usrA(p_enum_type text, p_new_val text) RETURNS void AS $$ BEGIN -- 先校验当前用户 IF current_user <> 'usrA' THEN RAISE EXCEPTION '权限不足:只有usrA用户才能修改此枚举类型'; END IF; -- 用dblink创建独立会话执行ALTER TYPE PERFORM dblink_connect('dbname=' || current_database()); PERFORM dblink_exec( 'ALTER TYPE ' || quote_ident(p_enum_type) || ' ADD VALUE ' || quote_literal(p_new_val) ); PERFORM dblink_disconnect(); END; $$ LANGUAGE plpgsql SECURITY DEFINER;
注意:
SECURITY DEFINER会让函数以创建者的权限执行,如果你担心安全风险,可以去掉这个选项,或者给函数设置严格的执行权限(比如只允许特定用户调用)。
使用的时候直接调用函数就行:
SELECT add_enum_value_for_usrA('enum_to_change', 'myNewValue');
总结
- 如果是用psql执行脚本,优先选方案1,简单高效无额外依赖;
- 如果需要嵌入到服务器端业务逻辑里,就用方案2的dblink方式,虽然多了个扩展依赖,但能满足服务器端校验的需求。
内容的提问来源于stack exchange,提问作者Kafka
相关产品推荐
相关产品推荐

