USE hstore store table's trace record

在数据库应用中,某些数据表的记录更改可能会需要跟踪,例如删除,新增,更新。

跟踪的信息包括:老的记录值,新的记录值,记录变更时间,哪个用户操作的等等。

在数据库中,跟踪可以通过触发器来做。

因为每个表的结构都不一样,要设计一个比较通用的存储跟踪记录的表的话,需要使用通用的存储类型,例如 text 类型,但是text类型存储的值未来要展现的话又比较费劲,还得根据表结构来抽取原来存储的变更信息,本文试图使用 hstore 类型来存储变更前后的row信息。并且使用hstore提供的each函数,可以很方便的取出原来存储的值。

测试 : 

-- 创建需要被跟踪的测试表

CREATE TABLE test (id int primary key, info text, crt_time timestamp(0));

-- 创建hstore extension;

CREATE EXTENSION hstore;

-- 创建通用的存储跟踪记录的记录表

CREATE TABLE table_change_rec (
id serial8 primary key,
relid oid,
table_schema text,
table_name text,
when_tg text,
level text,
op text,
old_rec hstore,
new_rec hstore,
crt_time timestamp without time zone DEFAULT now(),
username text,
client_addr inet,
client_port int
);

-- 创建通用的触发器函数

CREATE OR REPLACE FUNCTION dml_trace()
RETURNS trigger
LANGUAGE plpgsql
AS $BODY$
DECLARE
v_new_rec hstore;
v_old_rec hstore;
v_username text := session_user;
v_client_addr inet := inet_client_addr();
v_client_port int := inet_client_port();
BEGIN
case TG_OP
when 'DELETE' then
  v_old_rec := hstore(OLD.*);
  insert into table_change_rec (relid, table_schema, table_name, when_tg, level, op, old_rec, username, client_addr, client_port)
    values (tg_relid, tg_table_schema, tg_table_name, tg_when, tg_level, tg_op, v_old_rec, v_username, v_client_addr, v_client_port);
when 'INSERT' then
  v_new_rec := hstore(NEW.*);
  insert into table_change_rec (relid, table_schema, table_name, when_tg, level, op, new_rec, username, client_addr, client_port)
    values (tg_relid, tg_table_schema, tg_table_name, tg_when, tg_level, tg_op, v_new_rec, v_username, v_client_addr, v_client_port);
when 'UPDATE' then
  v_old_rec := hstore(OLD.*);
  v_new_rec := hstore(NEW.*);
  insert into table_change_rec (relid, table_schema, table_name, when_tg, level, op, old_rec, new_rec, username, client_addr, client_port)
    values (tg_relid, tg_table_schema, tg_table_name, tg_when, tg_level, tg_op, v_old_rec, v_new_rec, v_username, v_client_addr, v_client_port);
else
  return null;
end case;
  RETURN null;
END;
$BODY$ strict;

-- 在测试表上分别创建插入, 更新, 删除的三个触发器.

CREATE TRIGGER tg AFTER DELETE or INSERT or UPDATE ON test FOR EACH ROW EXECUTE PROCEDURE dml_trace();

-- 测试插入, 删除, 更新操作是否被跟踪.

(已更新dml_trace, 以下例子未包含client_addr和client_port)

postgres=# insert into test values (1, 'digoal', now());
INSERT 0 1
postgres=# select * from test;
 id |  info  |      crt_time
----+--------+---------------------
  1 | digoal | 2012-06-25 10:54:43
(1 row)

postgres=# select * from table_change_rec;
 id | relid | table_schema | table_name | when_tg | level |   op   | old_rec |                            new_rec
          |          crt_time          | username
----+-------+--------------+------------+---------+-------+--------+---------+------------------------------------------------------
----------+----------------------------+----------
  4 | 23731 | public       | test       | AFTER   | ROW   | INSERT |         | "id"=>"1", "info"=>"digoal", "crt_time"=>"2012-06-25
10:54:43" | 2012-06-25 10:54:42.839553 | postgres
(1 row)

postgres=# update test set info='DIGOAL' where id=1;
UPDATE 1
postgres=# select * from test;
 id |  info  |      crt_time
----+--------+---------------------
  1 | DIGOAL | 2012-06-25 10:54:43
(1 row)

postgres=# select * from table_change_rec;
 id | relid | table_schema | table_name | when_tg | level |   op   |                            old_rec
|                            new_rec                             |          crt_time          | username
----+-------+--------------+------------+---------+-------+--------+----------------------------------------------------------------
+----------------------------------------------------------------+----------------------------+----------
  4 | 23731 | public       | test       | AFTER   | ROW   | INSERT |
| "id"=>"1", "info"=>"digoal", "crt_time"=>"2012-06-25 10:54:43" | 2012-06-25 10:54:42.839553 | postgres
  5 | 23731 | public       | test       | AFTER   | ROW   | UPDATE | "id"=>"1", "info"=>"digoal", "crt_time"=>"2012-06-25 10:54:43"
| "id"=>"1", "info"=>"DIGOAL", "crt_time"=>"2012-06-25 10:54:43" | 2012-06-25 10:55:41.006069 | postgres
(2 rows)

postgres=# delete from test where id=1;
DELETE 1
postgres=# select * from test;
 id | info | crt_time
----+------+----------
(0 rows)

postgres=# select * from table_change_rec;
 id | relid | table_schema | table_name | when_tg | level |   op   |                            old_rec
|                            new_rec                             |          crt_time          | username
----+-------+--------------+------------+---------+-------+--------+----------------------------------------------------------------
+----------------------------------------------------------------+----------------------------+----------
  4 | 23731 | public       | test       | AFTER   | ROW   | INSERT |
| "id"=>"1", "info"=>"digoal", "crt_time"=>"2012-06-25 10:54:43" | 2012-06-25 10:54:42.839553 | postgres
  5 | 23731 | public       | test       | AFTER   | ROW   | UPDATE | "id"=>"1", "info"=>"digoal", "crt_time"=>"2012-06-25 10:54:43"
| "id"=>"1", "info"=>"DIGOAL", "crt_time"=>"2012-06-25 10:54:43" | 2012-06-25 10:55:41.006069 | postgres
  6 | 23731 | public       | test       | AFTER   | ROW   | DELETE | "id"=>"1", "info"=>"DIGOAL", "crt_time"=>"2012-06-25 10:54:43"
|                                                                | 2012-06-25 10:56:00.862319 | postgres
(3 rows)

-- 使用each函数分解显示hstore存储的信息.

postgres=# select id,(each(old_rec)).* from table_change_rec;
 id |   key    |        value
----+----------+---------------------
  5 | id       | 1
  5 | info     | digoal
  5 | crt_time | 2012-06-25 10:54:43
  6 | id       | 1
  6 | info     | DIGOAL
  6 | crt_time | 2012-06-25 10:54:43
(6 rows)

postgres=# select id,(each(new_rec)).* from table_change_rec;
 id |   key    |        value
----+----------+---------------------
  4 | id       | 1
  4 | info     | digoal
  4 | crt_time | 2012-06-25 10:54:43
  5 | id       | 1
  5 | info     | DIGOAL
  5 | crt_time | 2012-06-25 10:54:43
(6 rows)

【触发器变量】

When a PL/pgSQL function is called as a trigger, several special variables are created automatically in the top-level block. They are:

NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is NULL in statement-level triggers and for DELETE operations.

OLD
Data type RECORD; variable holding the old database row for UPDATE/DELETE operations in row-level triggers. This variable is NULL in statement-level triggers and for INSERT operations.

TG_NAME
Data type name; variable that contains the name of the trigger actually fired.

TG_WHEN
Data type text; a string of BEFORE, AFTER, or INSTEAD OF, depending on the trigger's definition.

TG_LEVEL
Data type text; a string of either ROW or STATEMENT depending on the trigger's definition.

TG_OP
Data type text; a string of INSERT, UPDATE, DELETE, or TRUNCATE telling for which operation the trigger was fired.

TG_RELID
Data type oid; the object ID of the table that caused the trigger invocation.

TG_RELNAME
Data type name; the name of the table that caused the trigger invocation. This is now deprecated, and could disappear in a future release. Use TG_TABLE_NAME instead.

TG_TABLE_NAME
Data type name; the name of the table that caused the trigger invocation.

TG_TABLE_SCHEMA
Data type name; the name of the schema of the table that caused the trigger invocation.

TG_NARGS
Data type integer; the number of arguments given to the trigger procedure in the CREATE TRIGGER statement.

TG_ARGV[]
Data type array of text; the arguments from the CREATE TRIGGER statement. The index counts from 0. Invalid indexes (less than 0 or greater than or equal to tg_nargs) result in a null value.

A trigger function must return either NULL or a record/row value having exactly the structure of the table the trigger was fired for.

【参考】
http://www.postgresql.org/docs/9.2/static/plpgsql-trigger.html
http://www.postgresql.org/docs/9.2/static/hstore.html

时间: 2024-10-22 05:26:45

USE hstore store table's trace record的相关文章

数据库案例集锦 - 开发者的《如来神掌》

背景 「剑魔独孤求败,纵横江湖三十馀载,杀尽仇寇,败尽英雄,天下更无抗手,无可柰何,惟隐居深谷,以雕为友.呜呼,生平求一敌手而不可得,诚寂寥难堪也.」 剑冢中,埋的是剑魔独孤求败毕生几个阶段中用过的几柄剑: 利剑无意:第一柄是青光闪闪的利剑,凌厉刚猛,无坚不摧,弱冠前以之与河朔群雄争锋. 软剑无常:第二柄是紫薇软剑,三十岁前所用,误伤义士不祥,悔恨不已,乃弃之深谷. 重剑无锋:第三柄是玄铁重剑,重剑无锋,大巧不工,四十岁之前恃之横行天下. 木剑无俦:第四柄是已腐朽的木剑. 无剑无招:四十岁后,不

又来勒索,有完没完 - 数据库安全指南

背景 数据库在一个企业中通常都处于非常核心的位置,数据库安全是一个非常严肃的话题. 从机房.网络.服务器.数据交换设备.操作系统.应用程序.数据库本身,数据库所处的环境非常复杂,安全隐患也非常多. 所以本文将从各个层面帮助大家理解和避免一些常见的安全隐患问题. 本文是PostgreSQL使用安全指导性的文章,涉及详细的用法或原理请参考相关链接. 如何安全的使用PostgreSQL,让用户高枕无忧呢? 可以分为如下几个方面来加固你的数据库. 一.认证安全 认证前的安全,端口暴露度的把握. Post

Linux内核跟踪之trace框架分析【转】

转自:http://blog.chinaunix.net/uid-20543183-id-1930846.html   ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ ------------------------------------------ 一: 前言 本文主要是对trace的框架做详尽的分析, 在后续的分析中,再来分析接入到框架中的几个重要的tra

ExtJs之Ext.data.Store

Ext.data.Store的基本用法 在使用之前,首先要创建一个Ext.data.Store的实例,如下面的代码所示.     1 var data = [ 2 ['boy', 0], 3 ['girl', 1] 4 ]; 5 6 var store = new Ext.data.Store({ 7 proxy: new Ext.data.MemoryProxy(data), 8 reader: new Ext.data.ArrayReader({}, PersonRecord) 9 });

一天学会PostgreSQL应用开发与管理 - 7 函数、存储过程和触发器

本章大纲 一.运算符与函数 1 逻辑运算 2 比较运算 3 算数 4 字符串 5 bytea 6 bit 7 规则表达式 8 日期.数字.字符串格式化输出 9 时间 10 枚举 11 几何 12 网络地址 13 全文检索 14 XML 15 JSON.JSONB 16 序列 17 条件表达式 18 数组 19 范围 20 聚合 21 窗口 22 子查询表达式 23 行与数组表达式 24 返回集合的函数 25 系统信息函数 26 系统管理函数 二.过程语言 1 语法 2 plpgsql函数内部结构

ExtJs 备忘录(6)—— GirdPanl表格(二) [ 搜索分页 ]

前言 近些天对于厚积薄发有深刻的理解,尤其是月末那两天,很想再写两篇文章,保持每周一篇--每月至少四篇以上的文章.写文章分两种情况:一种情况是已经积累了许多经验,写起来轻松且得心应手,内容和系列文章容易把握,最典型的就是视频监控的那系列文章,得以写完是因为已经从事近半年相关的开发工作;另一种情况则是有方向但积累不足甚至无积累,边学边实践,然后进行归类和总结成系列,比如视频监控第二个系列和本系列都属于这种情况,如果时间稍微充足且过程较为顺利,尚可勉强完成,反之则遥遥无期.由此感悟,下次写系列文章还

PostgreSQL flashback query 闪回功能 emulate by trigger

我之前写过一篇关于利用PostgreSQL mvcc特性来模拟闪回, 找回数据的文章, 有兴趣的同学可以参考如下 :  http://blog.163.com/digoal@126/blog/static/163877040201251911813661/ 使用以上方法需要担心的一个问题是数据可能被VACUUM掉了, 所以我们可以定制表的autovacuum threshold, 让重点保护的表的autovacuum 阈值较大, 减少VACUUM的间隔, 或者关闭重点保护的表的autovacuu

为什么要使用CQRS和Event Sourcing

Business value of the events, the value of having a log, the fact that the Event Store is additive only. The biggest advantage of this architecture is that it allows you to design your model to meet specific requirements. Typically, the requirements

DDL审计 , DDL逻辑复制 , 打造DDL统一管理入口 - PostgreSQL DDL 事件触发器应用

标签 PostgreSQL , event trigger , 事件触发器 , 用法详解 背景 DDL是非常重的操作,(锁大,或者会导致TABLE REWRITE导致消耗大量资源,影响大,例如DROP,TRUNCATE)也是数据库的使用过程中需要关注的. 通常企业会对DDL做一定的限制,不允许某些用户执行,或者不允许从网络登录的用户执行DDL. 同时DDL还有被审计的需求,所有的DDL可能都要记录到日志中. 构建逻辑复制的备库时,由于DDL不记录在REDO中,所以如果要复制DDL,通常的做法是使