当前位置: 首页 > 知识库问答 >
问题:

Postgresql:如何在数据库触发器中转义单引号?

翟冯浩
2023-03-14

我创建了一个数据库触发器来将行数据存储在审计表中。在更新操作期间,此触发器从主表中获取数据并将其插入历史表。(历史表有列:日期,操作类型说更新/删除,实际行数据)但在某些情况下,触发器会失败,因为输入数据中有引用的文本。

如何对触发器中的引用文本进行转义?

--My trigger
CREATE OR REPLACE FUNCTION audit.if_modified() RETURNS TRIGGER AS $function$
DECLARE
    temp_row RECORD; -- a temporary variable used on updates/deletes
    v_sql text;
BEGIN
    IF TG_WHEN <> 'AFTER' THEN
        RAISE EXCEPTION 'audit.if_modified() may only run as an AFTER trigger';
    END IF;

v_sql = 'select * from ' || TG_TABLE_NAME::regclass || '_history';
execute v_sql into temp_row;

select now() into temp_row.action_tstamp_tx;
temp_row.action = SUBSTRING(TG_OP,1,1);
IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
    temp_row.row_data = OLD;
ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
    temp_row.row_data = OLD;
ELSIF (TG_OP = 'INSERT' AND TG_LEVEL = 'ROW') THEN
    temp_row.row_data = NEW;
ELSE
    RAISE EXCEPTION '[audit.if_modified] - Trigger func added as trigger for unhandled case: %, %',TG_OP, TG_LEVEL;
    RETURN NULL;
END IF;

EXECUTE 'INSERT INTO audit.' || TG_TABLE_NAME::regclass || '_history VALUES (''' || 
temp_row.action_tstamp_tx || ''',''' ||
temp_row.action  || ''',''' ||
temp_row.row_data  || ''')'; 

RETURN NULL;
END;
$function$
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = audit,public,pg_catalog;

这对于正常用例很好,但是如果varchar数据有单引号文本,那么它无法将数据加载到历史表中。

ERROR:  syntax error at or near "s"
LINE 1: ...VALUES ('2016-02-22 11:44:43.994295-06','U','(6,Tom's,"2016-02...
                                                               ^
QUERY:  INSERT INTO audit.test_history VALUES ('2016-02-22 11:44:43.994295-06','U','(6,Tom's,"2016-02-22 09:49:32.315543")')
CONTEXT:  PL/pgSQL function if_modified() line 30 at EXECUTE

我是后格林的新手。我尝试过这样的选项

regexp_replace() API

SELECT into temp_row.row_data unnest(('{' || trim((temp_row.row_data)::text, '()') || '}')::text[]);

等,但我不知道如何循环ROWTYPE数据并创建正确的插入记录。

请分享您对如何编辑触发器以插入单引号文本的看法。

谢谢

共有1个答案

林波鸿
2023-03-14

一般来说,单引号是通过加倍来转义的。

要将变量连接到SQL字符串中,应使用quote_literal()-该函数负责正确转义单引号,例如:

quote_literal(temp_row.row_data)

话虽如此:更好(更安全)的解决方案是将参数与format()结合使用:

EXECUTE 
   format('INSERT INTO audit.%I_history values ($1, $2, $3)', tg_table_name)
   using temp_row.action_tstamp_tx, temp_row.action, temp_row.row_data; 

%I占位符通常会正确转义标识符,尽管在这种情况下它不起作用。如果要100%确保即使是非标准的表名也能正常工作,则需要首先将目标表名放入变量中,并将其用于format()函数:

l_tablename := TG_TABLE_NAME || '_history';
EXECUTE 
   format('INSERT INTO audit.%I_history values ($1, $2, $3)', l_tablename)
   using ....

这部分:

v_sql = 'select * from ' || TG_TABLE_NAME::regclass || '_history';
execute v_sql into temp_row;

在第一行之后也会失败。<代码>执行..到…里面...希望查询返回单个。您使用的语句将返回历史表中的所有行。

我也不明白你为什么要这样做。

您根本不需要从历史记录表中进行选择。

类似这样的应该就够了(未经测试!):

IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
    temp_row := OLD;
ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
    temp_row := OLD;
ELSIF (TG_OP = 'INSERT' AND TG_LEVEL = 'ROW') THEN
    temp_row := NEW;
ELSE
    RAISE EXCEPTION '[audit.if_modified] - Trigger func added as trigger for unhandled case: %, %',TG_OP, TG_LEVEL;
    RETURN NULL;
END IF;

execute format ('insert ... values ($1, $2, $3') 
   using now(), SUBSTRING(TG_OP,1,1), temp_row;

最后:审核触发器之前已经编写过,并且有很多现成的解决方案:

  • 使用hstore
  • 使用jsonb
  • Postgres Wiki中的一个复杂示例
 类似资料:
  • 与其在文本中转义每个,有没有办法一次转义它们,就像这样:

  • 问题内容: 更新: 我想给这个问题一个更新的答案。首先,让我说一下您是否要完成下面的工作,我建议您改为通过添加事件侦听器来管理事件。我强烈建议您将jQuery用于您的项目,并使用其语法来管理事件监听器,而不是使用DOM。 题 好的,我基本上是这样做的: 我不希望在双引号(“)放在'处。我只希望使用单引号,所以我尝试不使它在使用时放入双引号。我试图在最终结果中实现。 转义对我不起作用。 问题答案:

  • 问题内容: 如何在包含单引号或双引号的MySQL中插入一个值。即 单引号会产生问题。可能还有其他转义字符。 如何正确插入数据? 问题答案: 简单地说: 因此,在字符串中,将每个单引号替换为两个。 要么: 转义=)

  • 问题内容: 我来自MySQL,以下查询在Sybase中不起作用。我应该如何转义单引号? 问题答案: 如果使用Sybase,已经习惯了更多数据库用户有经验的MySQL,您可能很快就会发现,使用反斜杠无法转义单引号。 那么,如何在Sybase中转义引号呢?实际上,在Sybase SQL中,单引号充当转义字符。 请参见下面的两个``语言’‘中的UPDATE语句示例: 的MySQL Sybase公司 我不

  • PostgreSQL 触发器是数据库的回调函数,它会在指定的数据库事件发生时自动执行/调用。 下面是关于 PostgreSQL 触发器几个比较重要的点: PostgreSQL 触发器可以在下面几种情况下触发: 在执行操作之前(在检查约束并尝试插入、更新或删除之前)。 在执行操作之后(在检查约束并插入、更新或删除完成之后)。 更新操作(在对一个视图进行插入、更新、删除时)。 触发器的 FOR EAC

  • 问题内容: 我想用grep搜索看起来像这样的字符串: 我试过了,但是shell删除了单引号argh .. 什么是正确的搜索? 问题答案: 为我工作。 转义第一个以匹配文字的方式,而不是使其成为零个或多个匹配字符: 将匹配零个或多个匹配项,而while 匹配表达式之后 在周围使用双括号(请参见此处的示例) 使用后,不仅符合 一个 你单引号字符,但其中几 单引号根本不需要转义,因为它们包含在受双引号限