Séb*_*ent 7 postgresql dml plpgsql functions log
我有一个 pl/pgsql 函数(见下文),它列出了一些字段并使用动态构造的 UPDATE 命令清除它们的内容。
当我设置时log_statement = 'mod'
,我在执行函数时在日志上看不到任何内容SELECT fnct_clear_temp_fields();
。当我设置log_statement = 'all'
并执行我可以SELECT fnct_clear_temp_fields();
在日志中看到的函数时,但看不到底层的 UPDATE 命令。
有没有办法让 UPDATE 命令也出现在日志中?
有关信息,这里是功能:
CREATE OR REPLACE FUNCTION fnct_clear_temp_fields() RETURNS VOID AS $$
DECLARE
--Put into a cursor a view dynamically listing all user-defined fields beginning with 'temp_'
dataset_1 CURSOR FOR
SELECT
column_name,
table_name
FROM information_schema.tables
NATURAL JOIN information_schema.columns
WHERE
table_schema='public'
AND table_type='BASE TABLE'
AND column_name ~ '^temp_'
ORDER BY table_name,column_name;
--Record variable to go through each line of the view above
dataset_1_row RECORD;
BEGIN
OPEN dataset_1; --Open the cursor
FETCH dataset_1 INTO dataset_1_row; --first row of the view
WHILE FOUND LOOP
RAISE NOTICE 'Table: %, Column: %', dataset_1_row.table_name,dataset_1_row.column_name;
--Set to NULL the contents of the current 'temp_' column
EXECUTE 'UPDATE '||dataset_1_row.table_name||' SET '||dataset_1_row.column_name||'=NULL WHERE '||dataset_1_row.column_name||' IS NOT NULL';
FETCH dataset_1 INTO dataset_1_row; --next row please.
END LOOP; --while end
CLOSE dataset_1;
RETURN;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
这里是一个内置的方式来记录里面PLPGSQL功能的所有语句:auto-explain
LOAD 'auto_explain';
SET auto_explain.log_min_duration = 1; -- exclude very fast trivial queries
SET auto_explain.log_nested_statements = ON; -- statements inside functions
Run Code Online (Sandbox Code Playgroud)
在这个密切相关的问题下的详细信息:
用 pgpsql 编写的 UDF 调用的 Postgres 查询计划
可能会生成大量日志输出。我只会将它用于调试,而不是用于生产。
如果您只需要记录一个语句,请使用@dezso 的建议。
考虑这个重写的函数:
CREATE OR REPLACE FUNCTION fnct_clear_temp_fields()
RETURNS void AS
$func$
DECLARE
rec record;
qry text;
BEGIN
FOR rec IN
SELECT quote_ident(c.relname) AS tbl, quote_ident(a.attname) AS col
FROM pg_namespace n
JOIN pg_class c ON c.relnamespace = n.oid
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE n.nspname = 'public'
AND c.relkind = 'r'
AND a.attname LIKE 'temp_%' -- LIKE is faster than ~
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY 1,2
LOOP
RAISE NOTICE 'Table: %, Column: %', rec.tbl, rec.col;
qry := format('UPDATE %1$s SET %2$s = NULL WHERE %2$s IS NOT NULL', rec.tbl, rec.col);
RAISE LOG 'Query: %', qry;
EXECUTE qry;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
您必须清理您构建到动态 SQL 中的所有标识符,否则它可能会因需要双引号的非标准名称而失败。更糟糕的是,您对 SQL 注入持开放态度。
演示quote_ident()
,因为您多次使用经过消毒的标识符。有更多的选择与regclass
或format()
:
表名作为PostgreSQL的函数参数
我更喜欢将此类查询基于系统目录而不是信息模式的缓慢视图。不过,这是一个要求和品味的问题。演示等效项,它的速度提高了约 10 倍(与UPDATE
命令无关)。更多:
如何检查给定模式中是否存在表
LIKE
通常比更强大的正则表达式匹配 ( ~
)更快。如果LIKE
可以完成这项工作,请使用它。
其他一些小的简化。
有关更多详细信息的相关答案:
更新表名是参数的游标记录
所以,我的建议作为实际答案:
RAISE LOG '%', your_statement;
如果您仅在此函数中需要它,则可以在实际代码中执行, 或 :
...
DECLARE
exec_str text;
...
--Set to NULL the contents of the current 'temp_' column
exec_str := 'UPDATE '||dataset_1_row.table_name||
'SET '||dataset_1_row.column_name||'=NULL
WHERE '||dataset_1_row.column_name||' IS NOT NULL';
RAISE LOG 'Query executed: %', exec_str;
EXECUTE exec_str;
...
Run Code Online (Sandbox Code Playgroud)
另外,我发现
FOR dataset_1_row IN SELECT ...
LOOP
END LOOP;
Run Code Online (Sandbox Code Playgroud)
施工更加顺利。