亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > PostgreSQL > 正文

PostgreSQL7.0手冊-程序員手冊 -47. 觸發器

2019-09-08 23:34:18
字體:
來源:轉載
供稿:網友
第四十七章. 觸發器
內容 
創建觸發器 
與觸發器管理器交互 
數據改變的可視性 
例子 
Postgres 擁有多種客戶接口,象Perl,Tcl,Python 和 C,還有兩種 過程語言?。≒L).同樣也可能把 C 函數的調用作為觸發器的動作.要注意當前版本還不支持語句級(STATEMENT-level)的觸發器事件.目前你可以在 INSERT,DELETE 或 UPDATE 一條記錄上聲明 BEFORE 或 AFTER?。ㄖ盎蛑螅┳鳛橛|發器事件. 
創建觸發器
如果發生了觸發器事件,觸發器管理器(由執行器調用)初始化全局結構 TriggerData *CurrentTriggerData?。ㄏ旅婷枋觯┎⒄{用觸發器函數來操作事件. 
觸發器函數必須作為一個沒有參數并且返回 opaque 的函數在創建觸發器之前創建. 

創建觸發器的語法如下: 

CREATE TRIGGER trigger [ BEFORE | AFTER ] [ INSERT | DELETE | UPDATE [ OR ... ] ]
    ON relation FOR EACH [ ROW | STATEMENT ]
    EXECUTE PROCEDURE procedure
     (args);
這里的參數是: 
trigger 
如果你想刪除觸發器,那么這是所使用的觸發器的名稱.它被當做 DROP TRIGGER 命令的一個參數. 
BEFORE, AFTER 
決定函數是在事件之前還是之后調用. 
INSERT, DELETE, UPDATE 
命令的下一元素決定在什么事件上觸發該函數.多個事件可以用 OR 分隔聲明. 
relation 
關系名,決定該事件應用于哪個表. 
ROW, STATEMENT 
FOR EACH 子句決定該觸發器是為每個受影響的行觸發還是在整個語句完成之前(或之后)觸發. 
procedure 
過程名就是調用的 C 函數. 
args 
參數是放在 CurrentTriggerData 結構里面傳給函數的.傳遞參數給函數的目的是為了允許類似要求的不同的觸發器調用同樣的函數. 
  
  
  
  

同樣,函數可以被用于觸發不同的關系(這些函數被命名為"通用觸發器函數")。 

做為使用上面兩個特性的例子,可以有一個通用函數把兩個字段名稱作為參數:把當前用戶作為一個參數而把當前時標做為另一個參數.這樣就允許我們在 INSERT 事件上寫一個觸發器來自動跟蹤一個事務表里的記錄的創建.如果用于一個 UPDATE 事件,同樣我們可以當 "最后更新"(last updated)函數來用.

觸發器函數返回 HeapTuple 給調用它的執行器.這個返回在那些在 INSERT,DELETE 或 UPDATE 操作之后執行的觸發器上被忽略,但它允許那些 BEFORE 觸發器用來: 
返回 NULL 以忽略對當前記錄的操作(這樣該記錄就將不會被插入/更新/刪除). 
返回一個指向另一個記錄的指針(只用于 INSERT 和 UPDATE?。?,該指針所指記錄將代替原始記錄被插入(或者作為在 UPDATE 中記錄的新版本).

注意,CREATE TRIGGER 句柄將不進行任何初始化工作.這一點將在以后進行修改.同樣,如果多于一個觸發器為同樣的事件定義在同樣的關系上,觸發器觸發的順序將不可預料.這一點以后也會修改. 
如果一個觸發器函數執行 SQL-查詢(使用 SPI)那么這些查詢可能再次觸發觸發器.這就是所謂的嵌套觸發器.對嵌套觸發器的嵌套深度沒有顯式的限制. 

如果一個觸發器是被 INSERT 觸發并且插入一個新行到同一關系中,然后該觸發器將被再次觸發.目前對這種情況沒有提供任何同步(等)的措施,這一點也可能會修改.目前,回歸測試里有一個函數 funny_dup17() 使用了一些技巧避免對自身的遞歸(嵌套)調用...


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

與觸發器管理器交互
如我們前面所說,當觸發器管理器調用函數時,結構 TriggerData *CurrentTriggerData 是 NOT NULL (非空)的并且初始化過的.所以最好檢查 CurrentTriggerData 結構以防止在開始時就是 NULL?。眨┑牟⑶以讷@取信息之后把它清空以避免從非觸發器管理器來的觸發器函數. 
結構(struct)TriggerData 在 src/include/commands/trigger.h 里定義: 

typedef struct TriggerData
{
    TriggerEvent  tg_event;
    Relation      tg_relation;
    HeapTuple     tg_trigtuple;
    HeapTuple     tg_newtuple;
    Trigger      *tg_trigger;
} TriggerData;
這些成員的定義如下: 
tg_event 
描述調用函數的事件.你可以使用下面的宏來檢驗 tg_event:
TRIGGER_FIRED_BEFORE(tg_event) 
returns TRUE if trigger fired BEFORE.(觸發器由 BEFORE 觸發返回 TRUE) 
TRIGGER_FIRED_AFTER(tg_event) 
Returns TRUE if trigger fired AFTER.(觸發器由 AFTER 觸發返回 TRUE) 
TRIGGER_FIRED_FOR_ROW(event) 
Returns TRUE if trigger fired for a ROW-level event.(觸發器行級(ROW-level)觸發返回 TRUE) 
TRIGGER_FIRED_FOR_STATEMENT(event) 
Returns TRUE if trigger fired for STATEMENT-level event.(觸發器語句級(ROW-level)觸發返回 TRUE) 
TRIGGER_FIRED_BY_INSERT(event) 
Returns TRUE if trigger fired by INSERT.(觸發器由 INSERT 觸發返回 TRUE) 
TRIGGER_FIRED_BY_DELETE(event) 
Returns TRUE if trigger fired by DELETE.(觸發器由 DELETE 觸發返回 TRUE) 
TRIGGER_FIRED_BY_UPDATE(event) 
Returns TRUE if trigger fired by UPDATE.(觸發器由 UPDATE 觸發返回 TRUE)
tg_relation 
是一個指向描述被觸發的關系的結構的指針.請參考src/include/utils/rel.h 獲取關于此結構的詳細信息.最讓人感興趣的事情是 tg_relation->rd_att (關系記錄的描述) 和 tg_relation->rd_rel->relname?。P系名.這個變量的類型不是 char*,而是 NameData.用 SPI_getrelname(tg_relation) 獲取 char* ,如果你需要一份名字的拷貝的話). 
tg_trigtuple 
是一個指向觸發觸發器的記錄的指針.這是一個正在被 插入(INSERT),刪除(DELETE)或更新(UPDATE)的記錄.如果是 INSERT/DELETE ,那么這就是你將返回給執行器的東西--如果你不想用另一條記錄覆蓋此記錄(INSERT)或忽略操作. 
tg_newtuple 
如果是 UPDATE,這是一個指向新版本的記錄的指針,如果是 INSERT 或 DELETE,就是 NULL這就是你將返回給執行器的東西-- 如果你是 UPDATE 并且你不想用另一條記錄替換這條記錄或忽略操作. 
tg_trigger 
是一個指向結構 Trigger 的指針,該結構在 src/include/utils/rel.h 里定義: 
typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    FmgrInfo    tgfunc;
    int16       tgtype;
    bool        tgenabled;
    bool        tgisconstraint;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgattr[FUNC_MAX_ARGS];
    char      **tgargs;
} Trigger;
tgname 是觸發器的名稱,tgnargs 是在 tgargs 里參數的數量,tgargs 是一個指針數組,數組里每個指針指向在 CREATE TRIGGER 語句里聲明的參數.其他成員只在內部使用.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

數據改變的可視性
Postgres 數據修改的可視性規則:在查詢執行過程中,由查詢本身造成的數據修改(通過 SQL-函數,SPI-函數,觸發器)對查詢掃描而言是不可見的.例如,在查詢 
   INSERT INTO a SELECT * FROM a
里,插入的記錄對 SELECT 的掃描是不可見的.實際上,這么做在數據庫內部形成非遞歸的數據庫表的復制(當然是要受到唯一索引規則的制約的) 
但是請記住在 SPI 文擋里關于可視性的注釋: 

由查詢 Q 造成的改變可以為查詢 Q 以后運行的查詢可見,不管這些查詢 
是在查詢 Q 內部開始運行(在 Q 運行期間)的還是Q運行完畢后開始運行的
這些對觸發器而言也是正確的,盡管被插入的記錄?。╰g_trigtuple)對 BEFORE 觸發器是不可見的,這個剛被插入的記錄卻可以被一個 AFTER 觸發器看到,并且對所有這個(觸發器)以后的所有 BEFORE/AFTER 觸發器均可見!

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

例子
在 src/test/regress/regress.c 和 contrib/spi 里有更復雜的例子. 
這里是一個非常簡單的觸發器使用的例子.函數 trigf 報告在被觸發的關系 ttest 中記錄數量,并且如果查詢試圖把 NULL 插入到 x 里(例如?。鰹橐粋€ NOT NULL 約束但不退出事務的約束)時略過操作. 

#include "executor/spi.h"       /* this is what you need to work with SPI */
#include "commands/trigger.h"   /* -"- and triggers */

HeapTuple               trigf(void);

HeapTuple
trigf()
{
        TupleDesc       tupdesc;
        HeapTuple       rettuple;
        char            *when;
        bool            checknull = false;
        bool            isnull;
        int             ret, i;

        if (!CurrentTriggerData)
                elog(WARN, "trigf: triggers are not initialized");
        
        /* tuple to return to Executor */
        if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
                rettuple = CurrentTriggerData->tg_newtuple;
        else
                rettuple = CurrentTriggerData->tg_trigtuple;
        
        /* check for NULLs ? */
        if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) &&
                TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
                checknull = true;
        
        if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
                when = "before";
        else
                when = "after ";
        
        tupdesc = CurrentTriggerData->tg_relation->rd_att;
        CurrentTriggerData = NULL;
        
        /* Connect to SPI manager */
        if ((ret = SPI_connect()) < 0)
                elog(WARN, "trigf (fired %s): SPI_connect returned %d", when, ret);
        
        /* Get number of tuples in relation */
        ret = SPI_exec("select count(*) from ttest", 0);
        
        if (ret < 0)
                elog(WARN, "trigf (fired %s): SPI_exec returned %d", when, ret);
        
        i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
        
        elog (NOTICE, "trigf (fired %s): there are %d tuples in ttest", when, i);
        
        SPI_finish();
        
        if (checknull)
        {
                i = SPI_getbinval(rettuple, tupdesc, 1, &isnull);
                if (isnull)
                        rettuple = NULL;
        }

        return (rettuple);
}
然后,編譯和創建表 ttest (x int4): 
create function trigf () returns opaque as 
'...path_to_so' language 'c';
vac=> create trigger tbefore before insert or update or delete on ttest 
for each row execute procedure trigf();
CREATE
vac=> create trigger tafter after insert or update or delete on ttest 
for each row execute procedure trigf();
CREATE
vac=> insert into ttest values (null);
NOTICE:trigf (fired before): there are 0 tuples in ttest
INSERT 0 0

-- Insertion skipped and AFTER trigger is not fired

vac=> select * from ttest;
x
-
(0 rows)

vac=> insert into ttest values (1);
NOTICE:trigf (fired before): there are 0 tuples in ttest
NOTICE:trigf (fired after ): there are 1 tuples in ttest
                                       ^^^^^^^^
                             remember what we said about visibility.
INSERT 167793 1
vac=> select * from ttest;
x
-
1
(1 row)

vac=> insert into ttest select x * 2 from ttest;
NOTICE:trigf (fired before): there are 1 tuples in ttest
NOTICE:trigf (fired after ): there are 2 tuples in ttest
                                       ^^^^^^^^
                             remember what we said about visibility.
INSERT 167794 1
vac=> select * from ttest;
x
-
1
2
(2 rows)

vac=> update ttest set x = null where x = 2;
NOTICE:trigf (fired before): there are 2 tuples in ttest
UPDATE 0
vac=> update ttest set x = 4 where x = 2;
NOTICE:trigf (fired before): there are 2 tuples in ttest
NOTICE:trigf (fired after ): there are 2 tuples in ttest
UPDATE 1
vac=> select * from ttest;
x
-
1
4
(2 rows)

vac=> delete from ttest;
NOTICE:trigf (fired before): there are 2 tuples in ttest
NOTICE:trigf (fired after ): there are 1 tuples in ttest
NOTICE:trigf (fired before): there are 1 tuples in ttest
NOTICE:trigf (fired after ): there are 0 tuples in ttest
                                       ^^^^^^^^
                             remember what we said about visibility.
DELETE 2
vac=> select * from ttest;
x
-
(0 rows)

--------------------------------------------------------------------------------
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲第一区在线| 欧美午夜精品久久久久久久| 欧美亚洲另类视频| 欧美成人精品一区二区| 国产精品欧美日韩久久| 中文字幕日韩高清| 亚洲毛片在线看| 国产精品一区二区女厕厕| 久久精品欧美视频| 国产精品极品在线| 亚洲电影免费观看高清| 久久这里只有精品视频首页| 在线看欧美日韩| 欧美多人乱p欧美4p久久| 国产精品永久在线| 亚洲人成电影网站色| 综合网中文字幕| 亚洲人线精品午夜| 日本一区二三区好的精华液| 久久久久久久久久久久久久久久久久av| 亚洲国产一区二区三区四区| 精品福利樱桃av导航| 国内精品久久久久久影视8| 亚洲三级av在线| 欧美激情第三页| 国产精品久久久久久久久借妻| 精品国产鲁一鲁一区二区张丽| 欧美性生交xxxxxdddd| 日韩av最新在线观看| 亚洲美女喷白浆| 人九九综合九九宗合| 久久免费视频网| 啊v视频在线一区二区三区| 欧美亚洲国产日本| 国产99久久精品一区二区永久免费| 成人免费观看网址| 欧美精品成人91久久久久久久| 九九热99久久久国产盗摄| 琪琪亚洲精品午夜在线| 国产一区二区av| 日韩av影院在线观看| 久久中文字幕一区| 91高清视频免费观看| 成人激情春色网| 国产xxx69麻豆国语对白| 国产成人精品在线视频| 国产欧美日韩精品专区| 色综合久久精品亚洲国产| 久久久噜久噜久久综合| 欧美乱大交做爰xxxⅹ性3| 国产a∨精品一区二区三区不卡| 国产综合久久久久| 欧美怡红院视频一区二区三区| 亚洲伊人一本大道中文字幕| 国产精品扒开腿做爽爽爽男男| 97碰在线观看| 成人有码在线播放| 欧美xxxx18性欧美| 国产精品海角社区在线观看| 97香蕉超级碰碰久久免费软件| 欧美日韩xxx| 日韩av在线直播| 国产成人精品综合久久久| 国内精品视频久久| 国产成人精品视| 久久精品免费电影| 久久综合免费视频影院| 精品视频在线播放免| 精品久久久国产| 亚洲国产毛片完整版| 久久av在线看| 国产精品一区二区3区| 欧美成人性色生活仑片| 欧美日韩日本国产| 亚洲欧美在线看| 色偷偷888欧美精品久久久| 国产99久久精品一区二区 夜夜躁日日躁| 欧美精品做受xxx性少妇| 久久人人看视频| 久久久久久久爱| 日韩av123| 国产精品亚洲片夜色在线| 国产日韩欧美视频| 国产欧美日韩丝袜精品一区| 中文字幕日韩免费视频| 欧美一区二区三区免费观看| 国产精品久久久久久网站| 日韩av电影在线网| 国产精品视频内| 国产精品一区二区三区成人| 国产精品久久视频| 欧美电影免费观看高清| 97在线观看免费高清| 国产精品扒开腿爽爽爽视频| 国产精品久久久久久久久借妻| 亚洲福利在线看| 亚洲а∨天堂久久精品喷水| 91精品国产91久久久久久久久| 日韩av免费看| 韩剧1988免费观看全集| 欧美日韩激情视频| 91精品国产综合久久久久久蜜臀| 免费99精品国产自在在线| 欧美国产日韩一区二区在线观看| 高清一区二区三区日本久| 国产深夜精品福利| 亚洲欧美一区二区三区情侣bbw| 日韩中文字幕在线看| 麻豆精品精华液| 欧美日韩一区二区免费在线观看| 久久影视三级福利片| 美女扒开尿口让男人操亚洲视频网站| 国产精品99久久久久久www| 中文字幕精品久久久久| 欧美激情xxxx性bbbb| 欧美激情亚洲一区| 欧美黑人xxxx| 欧美性猛交99久久久久99按摩| 日韩欧美视频一区二区三区| 精品国产福利视频| 国产精品久久久久久av| 亚洲伊人第一页| 国内精品国产三级国产在线专| 91在线精品视频| 欧美限制级电影在线观看| 欧美日韩性视频在线| 92福利视频午夜1000合集在线观看| 亚洲精品网站在线播放gif| 久久精品视频99| 日韩欧美在线第一页| 欧美中文字幕视频| 3344国产精品免费看| 国产成人高潮免费观看精品| 中文字幕在线看视频国产欧美在线看完整| 欧美日韩成人黄色| 国产一区二区香蕉| 中文字幕在线视频日韩| 国产精品久久久999| 亚洲综合在线小说| 国产人妖伪娘一区91| 777午夜精品福利在线观看| 国产精品成人国产乱一区| 亚洲欧美一区二区三区四区| 久久久久久久97| 亚洲精品乱码久久久久久按摩观| 国产亚洲精品一区二555| 日韩在线www| 国产精品久久久久秋霞鲁丝| 亚洲激情中文字幕| 中文字幕日韩专区| 法国裸体一区二区| 国内外成人免费激情在线视频| 78色国产精品| 亲子乱一区二区三区电影| 久久久999国产精品| 色多多国产成人永久免费网站| 欧美激情视频三区| 成人在线激情视频| 免费91麻豆精品国产自产在线观看| 中文字幕亚洲欧美日韩高清| 国产成人精品国内自产拍免费看| 不卡av电影在线观看| 91久久夜色精品国产网站| 亚洲v日韩v综合v精品v|