hook.test 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. # 2004 Jan 14
  2. #
  3. # The author disclaims copyright to this source code. In place of
  4. # a legal notice, here is a blessing:
  5. #
  6. # May you do good and not evil.
  7. # May you find forgiveness for yourself and forgive others.
  8. # May you share freely, never taking more than you give.
  9. #
  10. #***********************************************************************
  11. # This file implements regression tests for TCL interface to the
  12. # SQLite library.
  13. #
  14. # The focus of the tests in this file is the following interface:
  15. #
  16. # sqlite_commit_hook (tests hook-1..hook-3 inclusive)
  17. # sqlite_update_hook (tests hook-4-*)
  18. # sqlite_rollback_hook (tests hook-5.*)
  19. #
  20. # $Id: hook.test,v 1.15 2009/04/07 14:14:23 danielk1977 Exp $
  21. set testdir [file dirname $argv0]
  22. source $testdir/tester.tcl
  23. do_test hook-1.2 {
  24. db commit_hook
  25. } {}
  26. do_test hook-3.1 {
  27. set commit_cnt 0
  28. proc commit_hook {} {
  29. incr ::commit_cnt
  30. return 0
  31. }
  32. db commit_hook ::commit_hook
  33. db commit_hook
  34. } {::commit_hook}
  35. do_test hook-3.2 {
  36. set commit_cnt
  37. } {0}
  38. do_test hook-3.3 {
  39. execsql {
  40. CREATE TABLE t2(a,b);
  41. }
  42. set commit_cnt
  43. } {1}
  44. do_test hook-3.4 {
  45. execsql {
  46. INSERT INTO t2 VALUES(1,2);
  47. INSERT INTO t2 SELECT a+1, b+1 FROM t2;
  48. INSERT INTO t2 SELECT a+2, b+2 FROM t2;
  49. }
  50. set commit_cnt
  51. } {4}
  52. do_test hook-3.5 {
  53. set commit_cnt {}
  54. proc commit_hook {} {
  55. set ::commit_cnt [execsql {SELECT * FROM t2}]
  56. return 0
  57. }
  58. execsql {
  59. INSERT INTO t2 VALUES(5,6);
  60. }
  61. set commit_cnt
  62. } {1 2 2 3 3 4 4 5 5 6}
  63. do_test hook-3.6 {
  64. set commit_cnt {}
  65. proc commit_hook {} {
  66. set ::commit_cnt [execsql {SELECT * FROM t2}]
  67. return 1
  68. }
  69. catchsql {
  70. INSERT INTO t2 VALUES(6,7);
  71. }
  72. } {1 {constraint failed}}
  73. verify_ex_errcode hook-3.6b SQLITE_CONSTRAINT_COMMITHOOK
  74. do_test hook-3.7 {
  75. set ::commit_cnt
  76. } {1 2 2 3 3 4 4 5 5 6 6 7}
  77. do_test hook-3.8 {
  78. execsql {SELECT * FROM t2}
  79. } {1 2 2 3 3 4 4 5 5 6}
  80. # Test turnning off the commit hook
  81. #
  82. do_test hook-3.9 {
  83. db commit_hook {}
  84. set ::commit_cnt {}
  85. execsql {
  86. INSERT INTO t2 VALUES(7,8);
  87. }
  88. set ::commit_cnt
  89. } {}
  90. # Ticket #3564.
  91. #
  92. do_test hook-3.10 {
  93. forcedelete test2.db test2.db-journal
  94. sqlite3 db2 test2.db
  95. proc commit_hook {} {
  96. set y [db2 one {SELECT y FROM t3 WHERE y>10}]
  97. return [expr {$y>10}]
  98. }
  99. db2 eval {CREATE TABLE t3(x,y)}
  100. db2 commit_hook commit_hook
  101. catchsql {INSERT INTO t3 VALUES(1,2)} db2
  102. catchsql {INSERT INTO t3 VALUES(11,12)} db2
  103. catchsql {INSERT INTO t3 VALUES(3,4)} db2
  104. db2 eval {
  105. SELECT * FROM t3 ORDER BY x;
  106. }
  107. } {1 2 3 4}
  108. db2 close
  109. #----------------------------------------------------------------------------
  110. # Tests for the update-hook.
  111. #
  112. # 4.1.* - Very simple tests. Test that the update hook is invoked correctly
  113. # for INSERT, DELETE and UPDATE statements, including DELETE
  114. # statements with no WHERE clause.
  115. # 4.2.* - Check that the update-hook is invoked for rows modified by trigger
  116. # bodies. Also that the database name is correctly reported when
  117. # an attached database is modified.
  118. # 4.3.* - Do some sorting, grouping, compound queries, population and
  119. # depopulation of indices, to make sure the update-hook is not
  120. # invoked incorrectly.
  121. #
  122. # Simple tests
  123. do_test hook-4.1.1 {
  124. catchsql {
  125. DROP TABLE t1;
  126. }
  127. execsql {
  128. CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  129. INSERT INTO t1 VALUES(1, 'one');
  130. INSERT INTO t1 VALUES(2, 'two');
  131. INSERT INTO t1 VALUES(3, 'three');
  132. }
  133. db update_hook [list lappend ::update_hook]
  134. } {}
  135. do_test hook-4.1.2 {
  136. execsql {
  137. INSERT INTO t1 VALUES(4, 'four');
  138. DELETE FROM t1 WHERE b = 'two';
  139. UPDATE t1 SET b = '' WHERE a = 1 OR a = 3;
  140. DELETE FROM t1 WHERE 1; -- Avoid the truncate optimization (for now)
  141. }
  142. set ::update_hook
  143. } [list \
  144. INSERT main t1 4 \
  145. DELETE main t1 2 \
  146. UPDATE main t1 1 \
  147. UPDATE main t1 3 \
  148. DELETE main t1 1 \
  149. DELETE main t1 3 \
  150. DELETE main t1 4 \
  151. ]
  152. ifcapable trigger {
  153. # Update hook is not invoked for changes to sqlite_master
  154. #
  155. do_test hook-4.1.3 {
  156. set ::update_hook {}
  157. execsql {
  158. CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT RAISE(IGNORE); END;
  159. }
  160. set ::update_hook
  161. } {}
  162. do_test hook-4.1.4 {
  163. set ::update_hook {}
  164. execsql {
  165. DROP TRIGGER r1;
  166. }
  167. set ::update_hook
  168. } {}
  169. set ::update_hook {}
  170. do_test hook-4.2.1 {
  171. catchsql {
  172. DROP TABLE t2;
  173. }
  174. execsql {
  175. CREATE TABLE t2(c INTEGER PRIMARY KEY, d);
  176. CREATE TRIGGER t1_trigger AFTER INSERT ON t1 BEGIN
  177. INSERT INTO t2 VALUES(new.a, new.b);
  178. UPDATE t2 SET d = d || ' via trigger' WHERE new.a = c;
  179. DELETE FROM t2 WHERE new.a = c;
  180. END;
  181. }
  182. } {}
  183. do_test hook-4.2.2 {
  184. execsql {
  185. INSERT INTO t1 VALUES(1, 'one');
  186. INSERT INTO t1 VALUES(2, 'two');
  187. }
  188. set ::update_hook
  189. } [list \
  190. INSERT main t1 1 \
  191. INSERT main t2 1 \
  192. UPDATE main t2 1 \
  193. DELETE main t2 1 \
  194. INSERT main t1 2 \
  195. INSERT main t2 2 \
  196. UPDATE main t2 2 \
  197. DELETE main t2 2 \
  198. ]
  199. } else {
  200. execsql {
  201. INSERT INTO t1 VALUES(1, 'one');
  202. INSERT INTO t1 VALUES(2, 'two');
  203. }
  204. }
  205. # Update-hook + ATTACH
  206. set ::update_hook {}
  207. ifcapable attach {
  208. do_test hook-4.2.3 {
  209. forcedelete test2.db
  210. execsql {
  211. ATTACH 'test2.db' AS aux;
  212. CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b);
  213. INSERT INTO aux.t3 SELECT * FROM t1;
  214. UPDATE t3 SET b = 'two or so' WHERE a = 2;
  215. DELETE FROM t3 WHERE 1; -- Avoid the truncate optimization (for now)
  216. }
  217. set ::update_hook
  218. } [list \
  219. INSERT aux t3 1 \
  220. INSERT aux t3 2 \
  221. UPDATE aux t3 2 \
  222. DELETE aux t3 1 \
  223. DELETE aux t3 2 \
  224. ]
  225. }
  226. ifcapable trigger {
  227. execsql {
  228. DROP TRIGGER t1_trigger;
  229. }
  230. }
  231. # Test that other vdbe operations involving btree structures do not
  232. # incorrectly invoke the update-hook.
  233. set ::update_hook {}
  234. do_test hook-4.3.1 {
  235. execsql {
  236. CREATE INDEX t1_i ON t1(b);
  237. INSERT INTO t1 VALUES(3, 'three');
  238. UPDATE t1 SET b = '';
  239. DELETE FROM t1 WHERE a > 1;
  240. }
  241. set ::update_hook
  242. } [list \
  243. INSERT main t1 3 \
  244. UPDATE main t1 1 \
  245. UPDATE main t1 2 \
  246. UPDATE main t1 3 \
  247. DELETE main t1 2 \
  248. DELETE main t1 3 \
  249. ]
  250. set ::update_hook {}
  251. ifcapable compound&&attach {
  252. do_test hook-4.3.2 {
  253. execsql {
  254. SELECT * FROM t1 UNION SELECT * FROM t3;
  255. SELECT * FROM t1 UNION ALL SELECT * FROM t3;
  256. SELECT * FROM t1 INTERSECT SELECT * FROM t3;
  257. SELECT * FROM t1 EXCEPT SELECT * FROM t3;
  258. SELECT * FROM t1 ORDER BY b;
  259. SELECT * FROM t1 GROUP BY b;
  260. }
  261. set ::update_hook
  262. } [list]
  263. }
  264. do_test hook-4.4 {
  265. execsql {
  266. CREATE TABLE t4(a UNIQUE, b);
  267. INSERT INTO t4 VALUES(1, 'a');
  268. INSERT INTO t4 VALUES(2, 'b');
  269. }
  270. set ::update_hook [list]
  271. execsql {
  272. REPLACE INTO t4 VALUES(1, 'c');
  273. }
  274. set ::update_hook
  275. } [list INSERT main t4 3 ]
  276. do_execsql_test hook-4.4.1 {
  277. SELECT * FROM t4 ORDER BY a;
  278. } {1 c 2 b}
  279. do_test hook-4.4.2 {
  280. set ::update_hook [list]
  281. execsql {
  282. PRAGMA recursive_triggers = on;
  283. REPLACE INTO t4 VALUES(1, 'd');
  284. }
  285. set ::update_hook
  286. } [list INSERT main t4 4 ]
  287. do_execsql_test hook-4.4.3 {
  288. SELECT * FROM t4 ORDER BY a;
  289. } {1 d 2 b}
  290. db update_hook {}
  291. #
  292. #----------------------------------------------------------------------------
  293. #----------------------------------------------------------------------------
  294. # Test the rollback-hook. The rollback-hook is a bit more complicated than
  295. # either the commit or update hooks because a rollback can happen
  296. # explicitly (an sql ROLLBACK statement) or implicitly (a constraint or
  297. # error condition).
  298. #
  299. # hook-5.1.* - Test explicit rollbacks.
  300. # hook-5.2.* - Test implicit rollbacks caused by constraint failure.
  301. #
  302. # hook-5.3.* - Test implicit rollbacks caused by IO errors.
  303. # hook-5.4.* - Test implicit rollbacks caused by malloc() failure.
  304. # hook-5.5.* - Test hot-journal rollbacks. Or should the rollback hook
  305. # not be called for these?
  306. #
  307. do_test hook-5.0 {
  308. # Configure the rollback hook to increment global variable
  309. # $::rollback_hook each time it is invoked.
  310. set ::rollback_hook 0
  311. db rollback_hook [list incr ::rollback_hook]
  312. } {}
  313. # Test explicit rollbacks. Not much can really go wrong here.
  314. #
  315. do_test hook-5.1.1 {
  316. set ::rollback_hook 0
  317. execsql {
  318. BEGIN;
  319. ROLLBACK;
  320. }
  321. set ::rollback_hook
  322. } {1}
  323. # Test implicit rollbacks caused by constraints.
  324. #
  325. do_test hook-5.2.1 {
  326. set ::rollback_hook 0
  327. catchsql {
  328. DROP TABLE t1;
  329. CREATE TABLE t1(a PRIMARY KEY, b);
  330. INSERT INTO t1 VALUES('one', 'I');
  331. INSERT INTO t1 VALUES('one', 'I');
  332. }
  333. set ::rollback_hook
  334. } {1}
  335. do_test hook-5.2.2 {
  336. # Check that the INSERT transaction above really was rolled back.
  337. execsql {
  338. SELECT count(*) FROM t1;
  339. }
  340. } {1}
  341. #
  342. # End rollback-hook testing.
  343. #----------------------------------------------------------------------------
  344. #----------------------------------------------------------------------------
  345. # Test that if a commit-hook returns non-zero (causing a rollback), the
  346. # rollback-hook is invoked.
  347. #
  348. proc commit_hook {} {
  349. lappend ::hooks COMMIT
  350. return 1
  351. }
  352. proc rollback_hook {} {
  353. lappend ::hooks ROLLBACK
  354. }
  355. do_test hook-6.1 {
  356. set ::hooks [list]
  357. db commit_hook commit_hook
  358. db rollback_hook rollback_hook
  359. catchsql {
  360. BEGIN;
  361. INSERT INTO t1 VALUES('two', 'II');
  362. COMMIT;
  363. }
  364. execsql { SELECT * FROM t1 }
  365. } {one I}
  366. do_test hook-6.2 {
  367. set ::hooks
  368. } {COMMIT ROLLBACK}
  369. unset ::hooks
  370. finish_test