假设使用下面的函数,我需要调试EXECUTE
语句中执行的内容。USING
(现实中提供动态变量的长动态语句)
CREATE OR REPLACE FUNCTION fn() RETURNS integer LANGUAGE 'plpgsql'
AS $$
BEGIN
EXECUTE $x$
SELECT $1, $2, $3
$x$ USING 1, '2020-01-01'::date, NULL::date;
RETURN 0;
END;
$$;
似乎在这种情况下,您能做的最好的事情就是取回字符串SELECT $1, $2, $3
。有没有办法打印/提高SELECT 1, '2020-01-01'::date, NULL::date
?
(我会想象类似的东西format('%1$s', 'xyz')
?)
这比人们预期的要复杂得多。PL/pgSQL 去除
EXECUTE
注释并解析查询字符串,识别标记。在计划查询之前,只有实际$
的 -parameters 会被子句中的值替换。USING
不$n
包含在注释、字符串或标识符(如-- $1 is required
、'My text says $2'
或"costs$3"
)中。我们必须复制解析器的大部分功能才能保持准确。但我过去一直希望有同样的功能。所以这里是:
Plain
replace()
会被任何$n
不是实际参数的情况所愚弄。这是一个穷人的实现,应该做得更好。它包括类型数据类型。(但它仍然远非完美!)首先是一个小辅助函数:Postgres 提供了几个函数来将字符串格式化为标识符、文字等:
我们需要这个:
但是对原始数据类型进行了附加转换。所以我称之为
quote_nullable_typed()
:format()
具有内联上述功能的格式说明符quote_*()
:%I
,%L
. 显然,不适用于我们的自定义功能。因此,明确应用它并使用%s
. 嵌套在另一个辅助函数中:该
VARIADIC
参数接受可变数量的参数。该函数迭代前 10 个(任意)。如果您需要更多,请展开列表。核心功能是:
加倍
%
准备format()
。看:正则表达式模式
(?<=[^[:alnum:]_''"])\$(\d+)(?=[^[:alnum:]_''"])
解释:(?<=)
... 正向后视[^[:alnum:]_''"]
... 字符类,不包括单词字符和_'"
\$
... 文字$
(\d+)
... 捕获括号中的一个或多个数字(注意替换)(?=)
... 正向前瞻因此,它仅
$n
在不在单词字符之间或引号旁边时替换。远非完美。但应涵盖典型案例。理想情况下,我们会先删除评论。谁有心情进一步改进它...
应用于您的原始示例:
第二个
NOTICE
将报告:db<>fiddle here - 带有扩展的测试用例
RAISE NOTICE
应该做的伎俩:要插入查询中的值,您可以使用