AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 71178
Accepted
Sébastien Clément
Sébastien Clément
Asked: 2014-07-11 07:55:52 +0800 CST2014-07-11 07:55:52 +0800 CST 2014-07-11 07:55:52 +0800 CST

如何记录由 pl/pgsql 函数执行的 DML 语句?

  • 772

我有一个 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;
postgresql functions
  • 3 3 个回答
  • 6953 Views

3 个回答

  • Voted
  1. Erwin Brandstetter
    2014-07-12T18:01:38+08:002014-07-12T18:01:38+08:00

    问的问题

    有一种内置方法可以记录 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
    

    这个密切相关的问题下的详细信息:
    用 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;
    

    要点

    • 您必须清理构建到动态 SQL 中的所有标识符,否则它可能会因需要双引号的非标准名称而失败。更糟糕的是,您对 SQL 注入持开放态度。
      演示quote_ident(),因为您多次使用经过清理的标识符。有更多选项regclassor format():
      表名作为 PostgreSQL 函数参数

    • 我更喜欢将此类查询基于系统目录,而不是信息模式的缓慢视图。不过,这是需求和品味的问题。演示等价物,大约快 10 倍(与UPDATE命令无关)。更多:
      如何检查给定模式中是否存在表

    • LIKE通常比更强大的正则表达式匹配 ( ~) 更快。如果LIKE可以完成这项工作,请使用它。

    • 其他一些小的简化。

    • 更多详细信息的相关答案:
      更新表名是参数的游标记录

    • 8
  2. Best Answer
    dezso
    2014-07-11T11:01:01+08:002014-07-11T11:01:01+08:00

    所以,我的建议是一个实际的答案:

    如果您仅在此函数中需要它,则可以执行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;
    ...
    

    另外,我发现

    FOR dataset_1_row IN SELECT ... 
    LOOP 
    END LOOP;
    

    构造更平滑。

    • 3
  3. Sébastien Clément
    2014-07-12T05:28:08+08:002014-07-12T05:28:08+08:00

    伟大的 dezso,它的工作原理!这是我的函数的最终版本:

    CREATE OR REPLACE FUNCTION fnct_clear_temp_fields() RETURNS VOID AS $$
    DECLARE
        dataset_1_row RECORD; --Record variable to go through each row of the view below
        update_query TEXT; --The dynamic UPDATE query to be executed
    
    BEGIN
        FOR dataset_1_row IN --Cycle through rows of query below
                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
            LOOP
    
            RAISE NOTICE 'Table: %, Column: %',  dataset_1_row.table_name,dataset_1_row.column_name;
            --Create a dynamic update query to set to NULL the contents of the current 'temp_' column
            update_query :='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: %', update_query; --Put query def in log
            EXECUTE update_query; --Run the query
        END LOOP; --Next line of SELECT query above 
    
        RETURN;
    END;
    $$ LANGUAGE plpgsql;
    
    • 1

相关问题

  • 我可以在使用数据库后激活 PITR 吗?

  • 运行时间偏移延迟复制的最佳实践

  • 存储过程可以防止 SQL 注入吗?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve