【轉】mysql觸發器的實戰經驗(觸發器執行失敗,sql會回滾嗎)
阿新 • • 發佈:2019-01-19
1 引言
Mysql的觸發器和儲存過程一樣,都是嵌入到mysql的一段程式。觸發器是mysql5新增的功能,目前線上鳳巢系統、北斗系統以及哥倫布系統使用的資料庫均是mysql5.0.45版本,很多程式比如fc-star管理端,sfrd(das),dorado都會用到觸發器程式,實現對於資料庫增、刪、改引起事件的關聯操作。本文介紹了觸發器的型別和基本使用方法,講述了觸發器使用中容易產生的誤區,從mysql原始碼中得到觸發器執行順序的結論,本文最後是實戰遭遇的觸發器經典案例。沒有特殊說明時,本文的實驗均基於mysql5.0.45版本。
2 Mysql觸發器的型別
2.1 Mysql觸發器的基本使用
建立觸發器。建立觸發器語法如下:
CREATE TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_stmt
其中trigger_name標識觸發器名稱,使用者自行指定;
trigger_time標識觸發時機,用before和after替換;
trigger_event標識觸發事件,用insert,update和delete替換;
tbl_name標識建立觸發器的表名,即在哪張表上建立觸發器;
trigger_stmt是觸發器程式體;觸發器程式可以使用begin和end作為開始和結束,中間包含多條語句;
下面給出sfrd一個觸發器例項:
CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER trig_useracct_update
AFTER UPDATE
ON SF_User.useracct FOR EACH ROW
BEGIN
IF OLD.ulevelid = 10101 OR OLD.ulevelid = 10104 THEN
IF NEW.ulevelid = 10101 OR NEW.ulevelid = 10104 THEN
if NEW.ustatid != OLD.ustatid OR NEW.exbudget != OLD.exbudget THEN
INSERT into FC_Output.fcevent set type = 2, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
end if;
ELSE
INSERT into FC_Output.fcevent set type = 1, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
END IF;
END IF;
END;
上述觸發器例項使用了OLD關鍵字和NEW關鍵字。OLD和NEW可以引用觸發器所在表的某一列,在上述例項中,OLD.ulevelid表示表 SF_User.useracct修改之前ulevelid列的值,NEW.ulevelid表示表SF_User.useracct修改之後 ulevelid列的值。另外,如果是insert型觸發器,NEW.ulevelid也表示表SF_User.useracct新增行的 ulevelid列值;如果是delete型觸發器OLD.ulevelid也表示表SF_User.useracct刪除行的ulevelid列原值。
另外,OLD列是隻讀的,NEW列則可以在觸發器程式中再次賦值。
上述例項也使用了IF,THEN ,ELSE,END IF等關鍵字。在觸發器程式體中,在beigin和end之間,可以使用順序,判斷,迴圈等語句,實現一般程式需要的邏輯功能。
檢視觸發器。檢視觸發器語法如下,如果知道觸發器所在資料庫,以及觸發器名稱等具體資訊:
SHOW TRIGGERS from SF_User like "usermaps%"; //檢視SF_User庫上名稱和usermaps%匹配的觸發器
如果不瞭解觸發器的具體的資訊,或者需要檢視資料庫上所有觸發器,如下:
SHOW TRIGGERS; //檢視所有觸發器
用上述方式檢視觸發器可以看到資料庫的所有觸發器,不過如果一個庫上的觸發器太多,由於會刷屏,可能沒有辦法檢視所有觸發器程式。這時,可以採用如下方式:
Mysql中有一個information_schema.TRIGGERS表,儲存所有庫中的所有觸發器,desc information_schema. TRIGGERS,可以看到表結構:
+----------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| TRIGGER_CATALOG | varchar(512) | YES | | NULL | |
| TRIGGER_SCHEMA | varchar(64) | NO | | | |
| TRIGGER_NAME | varchar(64) | NO | | | |
| EVENT_MANIPULATION | varchar(6) | NO | | | |
| EVENT_OBJECT_CATALOG | varchar(512) | YES | | NULL | |
| EVENT_OBJECT_SCHEMA | varchar(64) | NO | | | |
| EVENT_OBJECT_TABLE | varchar(64) | NO | | | |
| ACTION_ORDER | bigint(4) | NO | | 0 | |
| ACTION_CONDITION | longtext | YES | | NULL | |
| ACTION_STATEMENT | longtext | NO | | | |
| ACTION_ORIENTATION | varchar(9) | NO | | | |
| ACTION_TIMING | varchar(6) | NO | | | |
| ACTION_REFERENCE_OLD_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_NEW_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_OLD_ROW | varchar(3) | NO | | | |
| ACTION_REFERENCE_NEW_ROW | varchar(3) | NO | | | |
| CREATED | datetime | YES | | NULL | |
| SQL_MODE | longtext | NO | | | |
| DEFINER | longtext | NO | | | |
+----------------------------+--------------+------+-----+---------+-------+
這樣,使用者就可以按照自己的需要,檢視觸發器,比如使用如下語句檢視上述觸發器:
select * from information_schema. TRIGGERS where TRIGGER_NAME= 'trig_useracct_update'\G;
刪除觸發器。刪除觸發器語法如下:
DROP TRIGGER [schema_name.]trigger_name
2.2 Msyql觸發器的trigger_time和trigger_event
現在,重新注意到trigger_time和trigger_event,上文說過, trigger_time可以用before和after替換,表示觸發器程式的執行在sql執行的前還是後;trigger_event可以用 insert,update,delete替換,表示觸發器程式在什麼型別的sql下會被觸發。
在一個表上最多建立6個觸發器,即1)before insert型,2)before update型,3)before delete型,4)after insert型,5)after update型,6)after delete型。
觸發器的一個限制是不能同時在一個表上建立2個相同型別的觸發器。這個限制的一個來源是觸發器程式體的“begin和end之間允許執行多個語句”(摘自mysql使用手冊)。
另外還有一點需要注意,msyql除了對insert,update,delete基本操作進行定義外,還定義了load data和replace語句,而load data和replace語句也能引起上述6中型別的觸發器的觸發。
Load data語句用於將一個檔案裝入到一個數據表中,相當與一系列insert操作。replace語句一般來說和insert語句很像,只是在表中有 primary key和unique索引時,如果插入的資料和原來primary key和unique索引一致時,會先刪除原來的資料,然後增加一條新資料;也就是說,一條replace sql有時候等價於一條insert sql,有時候等價於一條delete sql加上一條insert sql。即是:
? Insert型觸發器:可能通過insert語句,load data語句,replace語句觸發;
? Update型觸發器:可能通過update語句觸發;
? Delete型觸發器:可能通過delete語句,replace語句觸發;
3 Mysql觸發器的執行順序
先丟擲觸發器相關的幾個問題
3.1 如果before型別的觸發器程式執行失敗,sql會執行成功嗎?
實驗如下:
1)在FC_Word.planinfo中建立before觸發器:
DELIMITER |
create trigger trigger_before_planinfo_update
before update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)檢視:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1; 觸發觸發器程式;
4)由於不存在FC_Output.abc,before觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次檢視:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果before觸發器執行失敗,sql也會執行失敗。
3.2 如果sql執行失敗,會執行after型別的觸發器程式嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
INSERT INTO FC_Output.fcevent set level = 2, type = 2, tabid = 5, userid = NEW.userid, planid = NEW.planid, planstat2 = NEW.planstat2, showprob = NEW.showprob, showrate = NEW.showrate, showfactor = NEW.showfactor, planmode = NEW.planmode;
END
|
2)檢視觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
沒有planid=1的記錄
3)執行sql:
mysql> update planinfo set showprob1=200 where planid=1;
4)由於不存在showprob1列,提示錯誤:
ERROR 1054 (42S22): Unknown column 'showprob1' in 'field list'
5)再次檢視觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
觸發表中沒有planid=1的記錄,sql在執行失敗時,after型觸發器不會執行。
3.3 如果after型別的觸發器程式執行失敗,sql會回滾嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)檢視:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1;觸發觸發器程式;
4)由於不存在FC_Output.abc,after觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次檢視:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果after觸發器執行失敗,sql會回滾。
這裡需要說明一下,上述實驗所使用的mysql引擎是innodb,innodb引擎也是目前線上鳳巢系統、北斗系統以及哥倫布系統所使用的引擎,在 innodb上所建立的表是事務性表,也就是事務安全的。“對於事務性表,如果觸發程式失敗(以及由此導致的整個語句的失敗),該語句所執行的所有更改將回滾。對於非事務性表,不能執行這類回滾”(摘自mysql使用手冊)。因而,即使語句失敗,失敗之前所作的任何更改依然有效,也就是說,對於 innodb引擎上的資料表,如果觸發器中的sql或引發觸發器的sql執行失效,則事務回滾,所有操作會失效。
3.4 mysql觸發器程式執行的順序
當一個表既有before型別的觸發器,又有after型別的觸發器時;當一條sql語句涉及多個表的update時,sql、觸發器的執行順序經過mysql原始碼包裝過,有時比較複雜。
可以先看一段mysql的原始碼,當SQL中update多表的時候,Mysql的執行過程如下(省去了無關程式碼):
/* 遍歷要更新的所有表 */
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
org_updated = updated
/* 如果有 BEFORE 觸發器,則執行;如果執行失敗,跳到err2位置 */
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,TRG_ACTION_BEFORE, TRUE))
goto err2;
/*執行更新,如果更新失敗,跳到err位置*/
if(local_error=table->file->update_row(table->record[1], table->record[0])))
goto err;
updated++; // 更新計數器
/* 如果有 AFTER 觸發器,則執行;如果執行失敗,跳到err2位置*/
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
goto err2;
err:
{
/*標誌錯誤資訊,寫日誌等*/
}
err2:
{
/*恢復執行過的操作*/
check_opt_it.rewind();
/*如果執行了更新,且表是有事務的,做標誌*/
if (updated != org_updated)
{
if (table->file->has_transactions())
transactional_tables= 1;
}
}
Mysql的觸發器和儲存過程一樣,都是嵌入到mysql的一段程式。觸發器是mysql5新增的功能,目前線上鳳巢系統、北斗系統以及哥倫布系統使用的資料庫均是mysql5.0.45版本,很多程式比如fc-star管理端,sfrd(das),dorado都會用到觸發器程式,實現對於資料庫增、刪、改引起事件的關聯操作。本文介紹了觸發器的型別和基本使用方法,講述了觸發器使用中容易產生的誤區,從mysql原始碼中得到觸發器執行順序的結論,本文最後是實戰遭遇的觸發器經典案例。沒有特殊說明時,本文的實驗均基於mysql5.0.45版本。
2 Mysql觸發器的型別
2.1 Mysql觸發器的基本使用
建立觸發器。建立觸發器語法如下:
CREATE TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_stmt
其中trigger_name標識觸發器名稱,使用者自行指定;
trigger_time標識觸發時機,用before和after替換;
trigger_event標識觸發事件,用insert,update和delete替換;
tbl_name標識建立觸發器的表名,即在哪張表上建立觸發器;
trigger_stmt是觸發器程式體;觸發器程式可以使用begin和end作為開始和結束,中間包含多條語句;
下面給出sfrd一個觸發器例項:
CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER trig_useracct_update
AFTER UPDATE
ON SF_User.useracct FOR EACH ROW
BEGIN
IF OLD.ulevelid = 10101 OR OLD.ulevelid = 10104 THEN
IF NEW.ulevelid = 10101 OR NEW.ulevelid = 10104 THEN
if NEW.ustatid != OLD.ustatid OR NEW.exbudget != OLD.exbudget THEN
INSERT into FC_Output.fcevent set type = 2, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
end if;
ELSE
INSERT into FC_Output.fcevent set type = 1, tabid = 1, level = 1, userid = NEW.userid, ustatid = NEW.ustatid, exbudget = NEW.exbudget;
END IF;
END IF;
END;
上述觸發器例項使用了OLD關鍵字和NEW關鍵字。OLD和NEW可以引用觸發器所在表的某一列,在上述例項中,OLD.ulevelid表示表 SF_User.useracct修改之前ulevelid列的值,NEW.ulevelid表示表SF_User.useracct修改之後 ulevelid列的值。另外,如果是insert型觸發器,NEW.ulevelid也表示表SF_User.useracct新增行的 ulevelid列值;如果是delete型觸發器OLD.ulevelid也表示表SF_User.useracct刪除行的ulevelid列原值。
另外,OLD列是隻讀的,NEW列則可以在觸發器程式中再次賦值。
上述例項也使用了IF,THEN ,ELSE,END IF等關鍵字。在觸發器程式體中,在beigin和end之間,可以使用順序,判斷,迴圈等語句,實現一般程式需要的邏輯功能。
檢視觸發器。檢視觸發器語法如下,如果知道觸發器所在資料庫,以及觸發器名稱等具體資訊:
SHOW TRIGGERS from SF_User like "usermaps%"; //檢視SF_User庫上名稱和usermaps%匹配的觸發器
如果不瞭解觸發器的具體的資訊,或者需要檢視資料庫上所有觸發器,如下:
SHOW TRIGGERS; //檢視所有觸發器
用上述方式檢視觸發器可以看到資料庫的所有觸發器,不過如果一個庫上的觸發器太多,由於會刷屏,可能沒有辦法檢視所有觸發器程式。這時,可以採用如下方式:
Mysql中有一個information_schema.TRIGGERS表,儲存所有庫中的所有觸發器,desc information_schema. TRIGGERS,可以看到表結構:
+----------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| TRIGGER_CATALOG | varchar(512) | YES | | NULL | |
| TRIGGER_SCHEMA | varchar(64) | NO | | | |
| TRIGGER_NAME | varchar(64) | NO | | | |
| EVENT_MANIPULATION | varchar(6) | NO | | | |
| EVENT_OBJECT_CATALOG | varchar(512) | YES | | NULL | |
| EVENT_OBJECT_SCHEMA | varchar(64) | NO | | | |
| EVENT_OBJECT_TABLE | varchar(64) | NO | | | |
| ACTION_ORDER | bigint(4) | NO | | 0 | |
| ACTION_CONDITION | longtext | YES | | NULL | |
| ACTION_STATEMENT | longtext | NO | | | |
| ACTION_ORIENTATION | varchar(9) | NO | | | |
| ACTION_TIMING | varchar(6) | NO | | | |
| ACTION_REFERENCE_OLD_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_NEW_TABLE | varchar(64) | YES | | NULL | |
| ACTION_REFERENCE_OLD_ROW | varchar(3) | NO | | | |
| ACTION_REFERENCE_NEW_ROW | varchar(3) | NO | | | |
| CREATED | datetime | YES | | NULL | |
| SQL_MODE | longtext | NO | | | |
| DEFINER | longtext | NO | | | |
+----------------------------+--------------+------+-----+---------+-------+
這樣,使用者就可以按照自己的需要,檢視觸發器,比如使用如下語句檢視上述觸發器:
select * from information_schema. TRIGGERS where TRIGGER_NAME= 'trig_useracct_update'\G;
刪除觸發器。刪除觸發器語法如下:
DROP TRIGGER [schema_name.]trigger_name
2.2 Msyql觸發器的trigger_time和trigger_event
現在,重新注意到trigger_time和trigger_event,上文說過, trigger_time可以用before和after替換,表示觸發器程式的執行在sql執行的前還是後;trigger_event可以用 insert,update,delete替換,表示觸發器程式在什麼型別的sql下會被觸發。
在一個表上最多建立6個觸發器,即1)before insert型,2)before update型,3)before delete型,4)after insert型,5)after update型,6)after delete型。
觸發器的一個限制是不能同時在一個表上建立2個相同型別的觸發器。這個限制的一個來源是觸發器程式體的“begin和end之間允許執行多個語句”(摘自mysql使用手冊)。
另外還有一點需要注意,msyql除了對insert,update,delete基本操作進行定義外,還定義了load data和replace語句,而load data和replace語句也能引起上述6中型別的觸發器的觸發。
Load data語句用於將一個檔案裝入到一個數據表中,相當與一系列insert操作。replace語句一般來說和insert語句很像,只是在表中有 primary key和unique索引時,如果插入的資料和原來primary key和unique索引一致時,會先刪除原來的資料,然後增加一條新資料;也就是說,一條replace sql有時候等價於一條insert sql,有時候等價於一條delete sql加上一條insert sql。即是:
? Insert型觸發器:可能通過insert語句,load data語句,replace語句觸發;
? Update型觸發器:可能通過update語句觸發;
? Delete型觸發器:可能通過delete語句,replace語句觸發;
3 Mysql觸發器的執行順序
先丟擲觸發器相關的幾個問題
3.1 如果before型別的觸發器程式執行失敗,sql會執行成功嗎?
實驗如下:
1)在FC_Word.planinfo中建立before觸發器:
DELIMITER |
create trigger trigger_before_planinfo_update
before update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)檢視:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1; 觸發觸發器程式;
4)由於不存在FC_Output.abc,before觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次檢視:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果before觸發器執行失敗,sql也會執行失敗。
3.2 如果sql執行失敗,會執行after型別的觸發器程式嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
INSERT INTO FC_Output.fcevent set level = 2, type = 2, tabid = 5, userid = NEW.userid, planid = NEW.planid, planstat2 = NEW.planstat2, showprob = NEW.showprob, showrate = NEW.showrate, showfactor = NEW.showfactor, planmode = NEW.planmode;
END
|
2)檢視觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
沒有planid=1的記錄
3)執行sql:
mysql> update planinfo set showprob1=200 where planid=1;
4)由於不存在showprob1列,提示錯誤:
ERROR 1054 (42S22): Unknown column 'showprob1' in 'field list'
5)再次檢視觸發表:
mysql> select * from FC_Output.fcevent where planid=1;
Empty set (0.00 sec)
觸發表中沒有planid=1的記錄,sql在執行失敗時,after型觸發器不會執行。
3.3 如果after型別的觸發器程式執行失敗,sql會回滾嗎?
實驗如下:
1)在FC_Word.planinfo中建立after觸發器:
DELIMITER |
create trigger trigger_after_planinfo_update
after update
ON FC_Word.planinfo FOR EACH ROW
BEGIN
insert into FC_Output.abc (planid) values (New.planid);
END
|
2)檢視:mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
3)執行sql:
update planinfo set showprob=200 where planid=1;觸發觸發器程式;
4)由於不存在FC_Output.abc,after觸發器執行失敗,提示:
ERROR 1146 (42S02): Table 'FC_Output.abc' doesn't exist
5)再次檢視:
mysql> select showprob from planinfo where planid=1;
+----------+
| showprob |
+----------+
| 2 |
+----------+
即修改sql未執行成功。即如果after觸發器執行失敗,sql會回滾。
這裡需要說明一下,上述實驗所使用的mysql引擎是innodb,innodb引擎也是目前線上鳳巢系統、北斗系統以及哥倫布系統所使用的引擎,在 innodb上所建立的表是事務性表,也就是事務安全的。“對於事務性表,如果觸發程式失敗(以及由此導致的整個語句的失敗),該語句所執行的所有更改將回滾。對於非事務性表,不能執行這類回滾”(摘自mysql使用手冊)。因而,即使語句失敗,失敗之前所作的任何更改依然有效,也就是說,對於 innodb引擎上的資料表,如果觸發器中的sql或引發觸發器的sql執行失效,則事務回滾,所有操作會失效。
3.4 mysql觸發器程式執行的順序
當一個表既有before型別的觸發器,又有after型別的觸發器時;當一條sql語句涉及多個表的update時,sql、觸發器的執行順序經過mysql原始碼包裝過,有時比較複雜。
可以先看一段mysql的原始碼,當SQL中update多表的時候,Mysql的執行過程如下(省去了無關程式碼):
/* 遍歷要更新的所有表 */
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
org_updated = updated
/* 如果有 BEFORE 觸發器,則執行;如果執行失敗,跳到err2位置 */
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,TRG_ACTION_BEFORE, TRUE))
goto err2;
/*執行更新,如果更新失敗,跳到err位置*/
if(local_error=table->file->update_row(table->record[1], table->record[0])))
goto err;
updated++; // 更新計數器
/* 如果有 AFTER 觸發器,則執行;如果執行失敗,跳到err2位置*/
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
goto err2;
err:
{
/*標誌錯誤資訊,寫日誌等*/
}
err2:
{
/*恢復執行過的操作*/
check_opt_it.rewind();
/*如果執行了更新,且表是有事務的,做標誌*/
if (updated != org_updated)
{
if (table->file->has_transactions())
transactional_tables= 1;
}
}