# PostgreSQL trigger 應用 保護table 並記錄 然後發送 telegram 通知 之前我們介紹了利用PostgreSQL trigger 的機制來保護table 不被update delete的方法,請參考: [點我前往](https://hackmd.io/@pgsql-tw/BJNU4QDYd) 現在我們來做些加強,除了保護以外,並增加記錄的功能. 先介紹將出現的 table 及 fucntion. 1. it0523b , it0523c 被保護的table 2. reject_log 紀錄被保護table 的delte , update 操作時間 3. f_reject_change() 用來保護的函數,會被trigger 呼叫 4. f_reject_log_modify() 將紀錄寫入 reject_log, 會被trigger 呼叫 ### 建立 table, function, trigger ```sql= create table it0523b ( id uuid not null default gen_random_uuid() , ts timestamp not null default clock_timestamp() , val int not null ); create table it0523c ( id uuid not null default gen_random_uuid() , ts timestamp not null default clock_timestamp() , val int not null ); create table reject_log ( id uuid not null default gen_random_uuid() , ts timestamp not null default clock_timestamp() , msg text ); -- functions create or replace function f_reject_change() returns trigger language plpgsql as $code$ begin return null; end; $code$; create or replace function f_reject_log_modify() returns trigger language plpgsql as $code$ begin insert into reject_log(msg) values (tg_table_schema || '.' || tg_table_name); return null; end; $code$; -- triggers create trigger tr_prot_it0523b before update or delete on it0523b for each row execute function f_reject_change(); create trigger tr_log_it0523b after update or delete on it0523b for each statement execute function f_reject_log_modify(); create trigger tr_prot_it0523c before update or delete on it0523c for each row execute function f_reject_change(); create trigger tr_log_it0523c after update or delete on it0523c for each statement execute function f_reject_log_modify(); commit; ``` 跟之前使用 for each row 的 trigger,這次使用 for each statement 的方式. ### 輸入資料及測試 ```sql= insert into it0523b(val) select generate_series(1, 3); delete from it0523b where val = 3; delete from it0523b; update it0523b set val = val + 1; select * from reject_log; id | ts | msg --------------------------------------+----------------------------+------------- cde86518-1c50-46d3-a051-0cfe803e791d | 2021-05-23 18:13:05.846341 | s12.it0523b 88851fd0-212f-4d64-888e-c87852c651e9 | 2021-05-23 18:13:37.667343 | s12.it0523b 1a224679-e80c-4e66-aad5-27d2ce955e36 | 2021-05-23 18:14:14.480931 | s12.it0523b (3 rows) ``` ### 測試結果討論 可以觀察到三次操作均有被紀錄下來, 以 schema.table 的方式紀錄了. ## 主動通知的方式 除了紀錄以外,若能夠主動通知就更理想了. 目前常用的通訊軟體除了Line, 還有 telegram . 以下將以 telegram 為例,至於 telegram bot 的 token以及chatid的設定取得方式,網路上有很多,在此不贅述. 我們將使用一個 plpython 函數,然後建立一個 rejct_log 的after insert trigger , 來達到此通知功能. ```sql= create or replace function f_send_reject_log() returns trigger language plpython3u as $code$ import requests bot_token = 'use the force' bot_chatID = 'luke' res = plpy.execute("select current_database()") curtdb = res[0]["current_database"] mesg = '有人想變動 database:' + curtdb + ' table: ' + TD["new"]["msg"] send_text = 'https://api.telegram.org/bot' + bot_token + '/sendMessage?chat_id=' + bot_chatID + '&parse_mode=Markdown&text=' + mesg requests.get(send_text) $code$; ``` ```sql= create trigger tr_send_reject_log after insert on reject_log for each row execute function f_send_reject_log(); ``` #### 測試 ```sql= update it0523b set val = val + 1; insert into it0523c(val) select generate_series(1, 3); update it0523c set val = val + 1; ``` #### 收到通知的畫面 ![收到通知的畫面](https://i.imgur.com/RuJxGad.png) ## 結論 由上面的結果來觀察,我們後續要增加被保護的table,無需再新建立function,只要再被保護的table 建立兩個trigger,呼叫原來的fucntion,就會保護以及紀錄,而紀錄的table又會驅動trigger function來做通知. PotgreSQL這樣的機制是很有彈性及靈活,這幾天有另外介紹了 extension temporal_tables 及 extension periods 都可以見到此種方式的應用,只需要通用funtion,就能搭配許多table. Trigger 在國內的應用較少討論,希望這幾篇event trigger 以及 trigger 的應用,能夠帶給大家一些參考.