在Postgres 14中,CREATE FUNCTION
增加了一种新的语法,函数体可以直接在SQL中指定,而不是包含在字符串中。函数的字符串体在pg_proc.prosrc
中很容易访问,而新的函数体似乎存储在pg_proc.prosqlbody
中。prosqlbody
列的类型为pg_node_tree
,通常会传递给pg_get_expr
,以便将其转换回可读的SQL。但是,在此列上调用pg_get_expr
的各种尝试要么返回NULL
,要么返回错误('input is a query,not an expression').这是一个bug,或者在这种情况下应该使用pg_get_expr
的替代方案?
我能想到的唯一的另一种方法是使用pg_get_functiondef
来获取整个CREATE FUNCTION
语句,然后遍历所有初始函数属性来找到函数体。这是可行的,但它感觉很脆弱,比我希望的要多。
上下文是我们正在编写一些代码来生成两个Postgres数据库之间的更新脚本-而当改变may not be an option的现有函数时,只使用来自pg_get_functiondef
的整个CREATE FUNCTION
语句并将其粘贴在像pg_dump
这样的脚本中会更简单。因此能够访问函数定义作为单独的部分非常有用。
2条答案
按热度按时间qncylg1j1#
高级解决方案
在源代码中有一个函数**
pg_get_function_sqlbody(oid)
。但它没有在标准Postgres中公开。如果你有足够的权限**(超级用户做的),你可以像这样从它创建一个LANGUAGE internal
函数:字符串
它接受一个函数的函数名,并完全按照您的要求执行:(仅)重构标准SQL函数的
sql_body
。任何角色都可以像这样调用这个自定义函数(以及其他方式):
型
在Postgres 15中进行了测试。在Postgres 14中可能也是如此。
由于该函数没有文档记录,因此您不能依赖主要版本的兼容性。项目可以根据需要自由删除或更改它(即使可能性很小)。
一些解释
引用手册,
pg_get_expr()
只有...反编译表达式的内部形式[...]
大胆强调我的。
标准SQL函数can either be的主体是单个语句:
型
或者一个block:
型
无论哪种方式,实际存储的是一个解析树,而不是一个简单的表达式。(尽管两者都使用了
pg_node_tree
数据类型。)所以pg_get_expr()
不能处理它,甚至不能处理“single statement”变体,你得到:型
(Or
null
(当以null
作为第二个参数调用时)。相关:
pg_get_functiondef()
是你在标准Postgres中的最佳选择。(在我看来是一个不错的选择。)8yoxcaq72#
@ErwinBrandstetter,谢谢你提供这个很棒的提示!我在本地启动并运行了自定义别名函数,没有问题。在我们的RDS部署中,没有超级用户访问,没有乐趣。
但这里有一些好消息:
pg_get_function_sqlbody(old)
直接工作 * 没有 * RDS上的自定义函数。字符串
稍后
我是为工作中的人们写这篇文章的,注意到
pg_get_functiondef()
和pg_get_function_sqlbody()
都可以工作,至少在15.5版本中是这样。输出不一样,pg_get_functiondef
的结果包括基本的CREATE OR REPLACE FUNCTION
标题。我猜这是偷偷进入一些点释放,或什么的。在任何情况下,它给了我另一个选择去思考。
型
x1c 0d1x的数据
再生功能
我浏览了更多的
pg_catalog
和系统信息函数,编写了一个例程来重新生成例程,基于存储在当前数据库中的详细信息。我在生成的代码体中发现了一些异常,这些代码体在系统之间和系统内部都不一致。比如,在一个UPSERT
中,EXCLUDED.
被更改为excluded_1
。不跟踪它,我甚至不确定我能不能追踪到它,我将使用我的源代码树手动构建一个补丁来重建例程。然而,如果你想尝试重新生成代码,并且只有SQL访问Postgres,那么这段代码有一堆你可能会发现有用的片段。我喜欢在那里爬来爬去.
型
下面是一个示例脚本的更新版本,它构建了一堆列表和代码块:
型