Home | History | Annotate | Download | only in test
      1 # 2008 December 15
      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 #
     12 # $Id: savepoint.test,v 1.13 2009/07/18 08:30:45 danielk1977 Exp $
     13 
     14 set testdir [file dirname $argv0]
     15 source $testdir/tester.tcl
     16 source $testdir/lock_common.tcl
     17 source $testdir/malloc_common.tcl
     18 
     19 #----------------------------------------------------------------------
     20 # The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE
     21 # and ROLLBACK TO comands are correctly parsed, and that the auto-commit
     22 # flag is correctly set and unset as a result.
     23 #
     24 do_test savepoint-1.1 {
     25   wal_set_journal_mode
     26   execsql {
     27     SAVEPOINT sp1;
     28     RELEASE sp1;
     29   }
     30 } {}
     31 do_test savepoint-1.2 {
     32   execsql {
     33     SAVEPOINT sp1;
     34     ROLLBACK TO sp1;
     35   }
     36 } {}
     37 do_test savepoint-1.3 {
     38   execsql { SAVEPOINT sp1 }
     39   db close
     40 } {}
     41 sqlite3 db test.db
     42 do_test savepoint-1.4.1 {
     43   execsql {
     44     SAVEPOINT sp1;
     45     SAVEPOINT sp2;
     46     RELEASE sp1;
     47   }
     48   sqlite3_get_autocommit db
     49 } {1}
     50 do_test savepoint-1.4.2 {
     51   execsql {
     52     SAVEPOINT sp1;
     53     SAVEPOINT sp2;
     54     RELEASE sp2;
     55   }
     56   sqlite3_get_autocommit db
     57 } {0}
     58 do_test savepoint-1.4.3 {
     59   execsql { RELEASE sp1 }
     60   sqlite3_get_autocommit db
     61 } {1}
     62 do_test savepoint-1.4.4 {
     63   execsql {
     64     SAVEPOINT sp1;
     65     SAVEPOINT sp2;
     66     ROLLBACK TO sp1;
     67   }
     68   sqlite3_get_autocommit db
     69 } {0}
     70 do_test savepoint-1.4.5 {
     71   execsql { RELEASE SAVEPOINT sp1 }
     72   sqlite3_get_autocommit db
     73 } {1}
     74 do_test savepoint-1.4.6 {
     75   execsql {
     76     SAVEPOINT sp1;
     77     SAVEPOINT sp2;
     78     SAVEPOINT sp3;
     79     ROLLBACK TO SAVEPOINT sp3;
     80     ROLLBACK TRANSACTION TO sp2;
     81     ROLLBACK TRANSACTION TO SAVEPOINT sp1;
     82   }
     83   sqlite3_get_autocommit db
     84 } {0}
     85 do_test savepoint-1.4.7 {
     86   execsql { RELEASE SAVEPOINT SP1 }
     87   sqlite3_get_autocommit db
     88 } {1}
     89 do_test savepoint-1.5 {
     90   execsql {
     91     SAVEPOINT sp1;
     92     ROLLBACK TO sp1;
     93   }
     94 } {}
     95 do_test savepoint-1.6 {
     96   execsql COMMIT
     97 } {}
     98 wal_check_journal_mode savepoint-1.7
     99 
    100 #------------------------------------------------------------------------
    101 # These tests - savepoint-2.* - test rollbacks and releases of savepoints
    102 # with a very simple data set.
    103 # 
    104 
    105 do_test savepoint-2.1 {
    106   execsql {
    107     CREATE TABLE t1(a, b, c);
    108     BEGIN;
    109     INSERT INTO t1 VALUES(1, 2, 3);
    110     SAVEPOINT one;
    111     UPDATE t1 SET a = 2, b = 3, c = 4;
    112   }
    113   execsql { SELECT * FROM t1 }
    114 } {2 3 4}
    115 do_test savepoint-2.2 {
    116   execsql {
    117     ROLLBACK TO one;
    118   }
    119   execsql { SELECT * FROM t1 }
    120 } {1 2 3}
    121 do_test savepoint-2.3 {
    122   execsql {
    123     INSERT INTO t1 VALUES(4, 5, 6);
    124   }
    125   execsql { SELECT * FROM t1 }
    126 } {1 2 3 4 5 6}
    127 do_test savepoint-2.4 {
    128   execsql {
    129     ROLLBACK TO one;
    130   }
    131   execsql { SELECT * FROM t1 }
    132 } {1 2 3}
    133 
    134 
    135 do_test savepoint-2.5 {
    136   execsql {
    137     INSERT INTO t1 VALUES(7, 8, 9);
    138     SAVEPOINT two;
    139     INSERT INTO t1 VALUES(10, 11, 12);
    140   }
    141   execsql { SELECT * FROM t1 }
    142 } {1 2 3 7 8 9 10 11 12}
    143 do_test savepoint-2.6 {
    144   execsql {
    145     ROLLBACK TO two;
    146   }
    147   execsql { SELECT * FROM t1 }
    148 } {1 2 3 7 8 9}
    149 do_test savepoint-2.7 {
    150   execsql {
    151     INSERT INTO t1 VALUES(10, 11, 12);
    152   }
    153   execsql { SELECT * FROM t1 }
    154 } {1 2 3 7 8 9 10 11 12}
    155 do_test savepoint-2.8 {
    156   execsql {
    157     ROLLBACK TO one;
    158   }
    159   execsql { SELECT * FROM t1 }
    160 } {1 2 3}
    161 do_test savepoint-2.9 {
    162   execsql {
    163     INSERT INTO t1 VALUES('a', 'b', 'c');
    164     SAVEPOINT two;
    165     INSERT INTO t1 VALUES('d', 'e', 'f');
    166   }
    167   execsql { SELECT * FROM t1 }
    168 } {1 2 3 a b c d e f}
    169 do_test savepoint-2.10 {
    170   execsql {
    171     RELEASE two;
    172   }
    173   execsql { SELECT * FROM t1 }
    174 } {1 2 3 a b c d e f}
    175 do_test savepoint-2.11 {
    176   execsql {
    177     ROLLBACK;
    178   }
    179   execsql { SELECT * FROM t1 }
    180 } {}
    181 wal_check_journal_mode savepoint-2.12
    182 
    183 #------------------------------------------------------------------------
    184 # This block of tests - savepoint-3.* - test that when a transaction
    185 # savepoint is rolled back, locks are not released from database files.
    186 # And that when a transaction savepoint is released, they are released.
    187 #
    188 # These tests do not work in WAL mode. WAL mode does not take RESERVED
    189 # locks on the database file.
    190 # 
    191 if {[wal_is_wal_mode]==0} {
    192   do_test savepoint-3.1 {
    193     execsql { SAVEPOINT "transaction" }
    194     execsql { PRAGMA lock_status }
    195   } {main unlocked temp closed}
    196   
    197   do_test savepoint-3.2 {
    198     execsql { INSERT INTO t1 VALUES(1, 2, 3) }
    199     execsql { PRAGMA lock_status }
    200   } {main reserved temp closed}
    201   
    202   do_test savepoint-3.3 {
    203     execsql { ROLLBACK TO "transaction" }
    204     execsql { PRAGMA lock_status }
    205   } {main reserved temp closed}
    206   
    207   do_test savepoint-3.4 {
    208     execsql { INSERT INTO t1 VALUES(1, 2, 3) }
    209     execsql { PRAGMA lock_status }
    210   } {main reserved temp closed}
    211   
    212   do_test savepoint-3.5 {
    213     execsql { RELEASE "transaction" }
    214     execsql { PRAGMA lock_status }
    215   } {main unlocked temp closed}
    216 }
    217 
    218 #------------------------------------------------------------------------
    219 # Test that savepoints that include schema modifications are handled
    220 # correctly. Test cases savepoint-4.*.
    221 # 
    222 do_test savepoint-4.1 {
    223   execsql {
    224     CREATE TABLE t2(d, e, f);
    225     SELECT sql FROM sqlite_master;
    226   }
    227 } {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}}
    228 do_test savepoint-4.2 {
    229   execsql {
    230     BEGIN;
    231     CREATE TABLE t3(g,h);
    232     INSERT INTO t3 VALUES('I', 'II');
    233     SAVEPOINT one;
    234     DROP TABLE t3;
    235   }
    236 } {}
    237 do_test savepoint-4.3 {
    238   execsql {
    239     CREATE TABLE t3(g, h, i);
    240     INSERT INTO t3 VALUES('III', 'IV', 'V');
    241   }
    242   execsql {SELECT * FROM t3}
    243 } {III IV V}
    244 do_test savepoint-4.4 {
    245   execsql { ROLLBACK TO one; }
    246   execsql {SELECT * FROM t3}
    247 } {I II}
    248 do_test savepoint-4.5 {
    249   execsql {
    250     ROLLBACK;
    251     SELECT sql FROM sqlite_master;
    252   }
    253 } {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}}
    254 
    255 do_test savepoint-4.6 {
    256   execsql {
    257     BEGIN;
    258     INSERT INTO t1 VALUES('o', 't', 't');
    259     SAVEPOINT sp1;
    260     CREATE TABLE t3(a, b, c);
    261     INSERT INTO t3 VALUES('z', 'y', 'x');
    262   }
    263   execsql {SELECT * FROM t3}
    264 } {z y x}
    265 do_test savepoint-4.7 {
    266   execsql {
    267     ROLLBACK TO sp1;
    268     CREATE TABLE t3(a);
    269     INSERT INTO t3 VALUES('value');
    270   }
    271   execsql {SELECT * FROM t3}
    272 } {value}
    273 do_test savepoint-4.8 {
    274   execsql COMMIT
    275 } {}
    276 wal_check_journal_mode savepoint-4.9
    277 
    278 #------------------------------------------------------------------------
    279 # Test some logic errors to do with the savepoint feature.
    280 # 
    281 
    282 ifcapable incrblob {
    283   do_test savepoint-5.1.1 {
    284     execsql {
    285       CREATE TABLE blobs(x);
    286       INSERT INTO blobs VALUES('a twentyeight character blob');
    287     }
    288     set fd [db incrblob blobs x 1]
    289     puts -nonewline $fd "hello"
    290     catchsql {SAVEPOINT abc}
    291   } {1 {cannot open savepoint - SQL statements in progress}}
    292   do_test savepoint-5.1.2 {
    293     close $fd
    294     catchsql {SAVEPOINT abc}
    295   } {0 {}}
    296   
    297   do_test savepoint-5.2 {
    298     execsql  {RELEASE abc}
    299     catchsql {RELEASE abc}
    300   } {1 {no such savepoint: abc}}
    301   
    302   do_test savepoint-5.3.1 {
    303     execsql  {SAVEPOINT abc}
    304     catchsql {ROLLBACK TO def}
    305   } {1 {no such savepoint: def}}
    306   do_test savepoint-5.3.2 {
    307     execsql  {SAVEPOINT def}
    308     set fd [db incrblob -readonly blobs x 1]
    309     catchsql {ROLLBACK TO def}
    310   } {1 {cannot rollback savepoint - SQL statements in progress}}
    311   do_test savepoint-5.3.3 {
    312     catchsql  {RELEASE def}
    313   } {0 {}}
    314   do_test savepoint-5.3.4 {
    315     close $fd
    316     execsql  {savepoint def}
    317     set fd [db incrblob blobs x 1]
    318     catchsql {release def}
    319   } {1 {cannot release savepoint - SQL statements in progress}}
    320   do_test savepoint-5.3.5 {
    321     close $fd
    322     execsql {release abc}
    323   } {}
    324   
    325   # Rollback mode:
    326   #
    327   #   Open a savepoint transaction and insert a row into the database. Then,
    328   #   using a second database handle, open a read-only transaction on the
    329   #   database file. Check that the savepoint transaction cannot be committed
    330   #   until after the read-only transaction has been closed.
    331   #
    332   # WAL mode:
    333   # 
    334   #   As above, except that the savepoint transaction can be successfully
    335   #   committed before the read-only transaction has been closed.
    336   #
    337   do_test savepoint-5.4.1 {
    338     execsql {
    339       SAVEPOINT main;
    340       INSERT INTO blobs VALUES('another blob');
    341     }
    342   } {}
    343   do_test savepoint-5.4.2 {
    344     sqlite3 db2 test.db
    345     execsql { BEGIN ; SELECT count(*) FROM blobs } db2
    346   } {1}
    347   if {[wal_is_wal_mode]} {
    348     do_test savepoint-5.4.3 { catchsql "RELEASE main" } {0 {}}
    349     do_test savepoint-5.4.4 { db2 close               } {}
    350   } else {
    351     do_test savepoint-5.4.3 {
    352       catchsql { RELEASE main }
    353     } {1 {database is locked}}
    354     do_test savepoint-5.4.4 {
    355       db2 close
    356       catchsql { RELEASE main }
    357     } {0 {}}
    358   }
    359   do_test savepoint-5.4.5 {
    360     execsql { SELECT x FROM blobs WHERE rowid = 2 }
    361   } {{another blob}}
    362   do_test savepoint-5.4.6 {
    363     execsql { SELECT count(*) FROM blobs }
    364   } {2}
    365 }
    366 wal_check_journal_mode savepoint-5.5
    367 
    368 #-------------------------------------------------------------------------
    369 # The following tests, savepoint-6.*, test an incr-vacuum inside of a
    370 # couple of nested savepoints.
    371 #
    372 ifcapable {autovacuum && pragma} {
    373   db close
    374   file delete -force test.db
    375   sqlite3 db test.db
    376 
    377   do_test savepoint-6.1 {
    378     execsql { PRAGMA auto_vacuum = incremental }
    379     wal_set_journal_mode
    380     execsql {
    381       CREATE TABLE t1(a, b, c);
    382       CREATE INDEX i1 ON t1(a, b);
    383       BEGIN;
    384       INSERT INTO t1 VALUES(randstr(10,400),randstr(10,400),randstr(10,400));
    385     }
    386     set r "randstr(10,400)"
    387     for {set ii 0} {$ii < 10} {incr ii} {
    388       execsql "INSERT INTO t1 SELECT $r, $r, $r FROM t1"
    389     }
    390     execsql { COMMIT }
    391   } {}
    392 
    393   integrity_check savepoint-6.2
    394 
    395   do_test savepoint-6.3 {
    396     execsql {
    397       PRAGMA cache_size = 10;
    398       BEGIN;
    399         UPDATE t1 SET a = randstr(10,10) WHERE (rowid%4)==0;
    400         SAVEPOINT one;
    401           DELETE FROM t1 WHERE rowid%2;
    402           PRAGMA incr_vacuum;
    403           SAVEPOINT two;
    404             INSERT INTO t1 SELECT randstr(10,400), randstr(10,400), c FROM t1;
    405             DELETE FROM t1 WHERE rowid%2;
    406             PRAGMA incr_vacuum;
    407         ROLLBACK TO one;
    408       COMMIT;
    409     }
    410   } {}
    411 
    412   integrity_check savepoint-6.4
    413 
    414   wal_check_journal_mode savepoint-6.5
    415 }
    416 
    417 #-------------------------------------------------------------------------
    418 # The following tests, savepoint-7.*, attempt to break the logic 
    419 # surrounding savepoints by growing and shrinking the database file.
    420 #
    421 db close
    422 file delete -force test.db
    423 sqlite3 db test.db
    424 
    425 do_test savepoint-7.1 {
    426   execsql { PRAGMA auto_vacuum = incremental }
    427   wal_set_journal_mode
    428   execsql {
    429     PRAGMA cache_size = 10;
    430     BEGIN;
    431     CREATE TABLE t1(a PRIMARY KEY, b);
    432       INSERT INTO t1(a) VALUES('alligator');
    433       INSERT INTO t1(a) VALUES('angelfish');
    434       INSERT INTO t1(a) VALUES('ant');
    435       INSERT INTO t1(a) VALUES('antelope');
    436       INSERT INTO t1(a) VALUES('ape');
    437       INSERT INTO t1(a) VALUES('baboon');
    438       INSERT INTO t1(a) VALUES('badger');
    439       INSERT INTO t1(a) VALUES('bear');
    440       INSERT INTO t1(a) VALUES('beetle');
    441       INSERT INTO t1(a) VALUES('bird');
    442       INSERT INTO t1(a) VALUES('bison');
    443       UPDATE t1 SET b =    randstr(1000,1000);
    444       UPDATE t1 SET b = b||randstr(1000,1000);
    445       UPDATE t1 SET b = b||randstr(1000,1000);
    446       UPDATE t1 SET b = b||randstr(10,1000);
    447     COMMIT;
    448   }
    449   expr ([execsql { PRAGMA page_count }] > 20)
    450 } {1}
    451 do_test savepoint-7.2.1 {
    452   execsql {
    453     BEGIN;
    454       SAVEPOINT one;
    455       CREATE TABLE t2(a, b);
    456       INSERT INTO t2 SELECT a, b FROM t1;
    457       ROLLBACK TO one;
    458   }
    459   execsql {
    460     PRAGMA integrity_check;
    461   }
    462 } {ok}
    463 do_test savepoint-7.2.2 {
    464   execsql {
    465     COMMIT;
    466     PRAGMA integrity_check;
    467   }
    468 } {ok}
    469 
    470 do_test savepoint-7.3.1 {
    471   execsql {
    472     CREATE TABLE t2(a, b);
    473     INSERT INTO t2 SELECT a, b FROM t1;
    474   }
    475 } {}
    476 do_test savepoint-7.3.2 {
    477   execsql {
    478     BEGIN;
    479       SAVEPOINT one;
    480         DELETE FROM t2;
    481         PRAGMA incremental_vacuum;
    482         SAVEPOINT two;
    483           INSERT INTO t2 SELECT a, b FROM t1;
    484         ROLLBACK TO two;
    485     COMMIT;
    486   }
    487   execsql { PRAGMA integrity_check }
    488 } {ok}
    489 wal_check_journal_mode savepoint-7.3.3
    490 
    491 do_test savepoint-7.4.1 {
    492   db close
    493   file delete -force test.db
    494   sqlite3 db test.db
    495   execsql { PRAGMA auto_vacuum = incremental }
    496   wal_set_journal_mode
    497   execsql {
    498     CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
    499     INSERT INTO t1 VALUES(randstr(1000,1000), randstr(1000,1000));
    500     BEGIN;
    501       DELETE FROM t1;
    502       SAVEPOINT one;
    503       PRAGMA incremental_vacuum;
    504       ROLLBACK TO one;
    505     COMMIT;
    506   }
    507 
    508   execsql { PRAGMA integrity_check }
    509 } {ok}
    510 
    511 do_test savepoint-7.5.1 {
    512   execsql {
    513     PRAGMA incremental_vacuum;
    514     CREATE TABLE t5(x, y);
    515     INSERT INTO t5 VALUES(1, randstr(1000,1000));
    516     INSERT INTO t5 VALUES(2, randstr(1000,1000));
    517     INSERT INTO t5 VALUES(3, randstr(1000,1000));
    518 
    519     BEGIN;
    520       INSERT INTO t5 VALUES(4, randstr(1000,1000));
    521       INSERT INTO t5 VALUES(5, randstr(1000,1000));
    522       DELETE FROM t5 WHERE x=1 OR x=2;
    523       SAVEPOINT one;
    524         PRAGMA incremental_vacuum;
    525         SAVEPOINT two;
    526           INSERT INTO t5 VALUES(1, randstr(1000,1000));
    527           INSERT INTO t5 VALUES(2, randstr(1000,1000));
    528         ROLLBACK TO two;
    529       ROLLBACK TO one;
    530     COMMIT;
    531     PRAGMA integrity_check;
    532   }
    533 } {ok}
    534 do_test savepoint-7.5.2 {
    535   execsql {
    536     DROP TABLE t5;
    537   }
    538 } {}
    539 wal_check_journal_mode savepoint-7.5.3
    540 
    541 # Test oddly named and quoted savepoints.
    542 #
    543 do_test savepoint-8-1 {
    544   execsql { SAVEPOINT "save1" }
    545   execsql { RELEASE save1 }
    546 } {}
    547 do_test savepoint-8-2 {
    548   execsql { SAVEPOINT "Including whitespace " }
    549   execsql { RELEASE "including Whitespace " }
    550 } {}
    551 
    552 # Test that the authorization callback works.
    553 #
    554 ifcapable auth {
    555   proc auth {args} {
    556     eval lappend ::authdata $args
    557     return SQLITE_OK
    558   }
    559   db auth auth
    560 
    561   do_test savepoint-9.1 {
    562     set ::authdata [list]
    563     execsql { SAVEPOINT sp1 }
    564     set ::authdata
    565   } {SQLITE_SAVEPOINT BEGIN sp1 {} {}}
    566   do_test savepoint-9.2 {
    567     set ::authdata [list]
    568     execsql { ROLLBACK TO sp1 }
    569     set ::authdata
    570   } {SQLITE_SAVEPOINT ROLLBACK sp1 {} {}}
    571   do_test savepoint-9.3 {
    572     set ::authdata [list]
    573     execsql { RELEASE sp1 }
    574     set ::authdata
    575   } {SQLITE_SAVEPOINT RELEASE sp1 {} {}}
    576 
    577   proc auth {args} {
    578     eval lappend ::authdata $args
    579     return SQLITE_DENY
    580   }
    581   db auth auth
    582 
    583   do_test savepoint-9.4 {
    584     set ::authdata [list]
    585     set res [catchsql { SAVEPOINT sp1 }]
    586     concat $::authdata $res
    587   } {SQLITE_SAVEPOINT BEGIN sp1 {} {} 1 {not authorized}}
    588   do_test savepoint-9.5 {
    589     set ::authdata [list]
    590     set res [catchsql { ROLLBACK TO sp1 }]
    591     concat $::authdata $res
    592   } {SQLITE_SAVEPOINT ROLLBACK sp1 {} {} 1 {not authorized}}
    593   do_test savepoint-9.6 {
    594     set ::authdata [list]
    595     set res [catchsql { RELEASE sp1 }]
    596     concat $::authdata $res
    597   } {SQLITE_SAVEPOINT RELEASE sp1 {} {} 1 {not authorized}}
    598 
    599   catch { db eval ROLLBACK }
    600   db auth ""
    601 }
    602 
    603 #-------------------------------------------------------------------------
    604 # The following tests - savepoint-10.* - test the interaction of 
    605 # savepoints and ATTACH statements.
    606 # 
    607 
    608 # First make sure it is not possible to attach or detach a database while
    609 # a savepoint is open (it is not possible if any transaction is open).
    610 #
    611 do_test savepoint-10.1.1 {
    612   catchsql {
    613     SAVEPOINT one;
    614     ATTACH 'test2.db' AS aux;
    615   }
    616 } {1 {cannot ATTACH database within transaction}}
    617 do_test savepoint-10.1.2 {
    618   execsql {
    619     RELEASE one;
    620     ATTACH 'test2.db' AS aux;
    621   }
    622   catchsql {
    623     SAVEPOINT one;
    624     DETACH aux;
    625   }
    626 } {1 {cannot DETACH database within transaction}}
    627 do_test savepoint-10.1.3 {
    628   execsql {
    629     RELEASE one;
    630     DETACH aux;
    631   }
    632 } {}
    633 
    634 # The lock state of the TEMP database can vary if SQLITE_TEMP_STORE=3
    635 # And the following set of tests is only really interested in the status
    636 # of the aux1 and aux2 locks.  So record the current lock status of
    637 # TEMP for use in the answers.
    638 set templockstate [lindex [db eval {PRAGMA lock_status}] 3]
    639 
    640 
    641 if {[wal_is_wal_mode]==0} {
    642   do_test savepoint-10.2.1 {
    643     file delete -force test3.db
    644     file delete -force test2.db
    645     execsql {
    646       ATTACH 'test2.db' AS aux1;
    647       ATTACH 'test3.db' AS aux2;
    648       DROP TABLE t1;
    649       CREATE TABLE main.t1(x, y);
    650       CREATE TABLE aux1.t2(x, y);
    651       CREATE TABLE aux2.t3(x, y);
    652       SELECT name FROM sqlite_master 
    653         UNION ALL
    654       SELECT name FROM aux1.sqlite_master 
    655         UNION ALL
    656       SELECT name FROM aux2.sqlite_master;
    657     }
    658   } {t1 t2 t3}
    659   do_test savepoint-10.2.2 {
    660     execsql { PRAGMA lock_status }
    661   } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
    662   
    663   do_test savepoint-10.2.3 {
    664     execsql {
    665       SAVEPOINT one;
    666       INSERT INTO t1 VALUES(1, 2);
    667       PRAGMA lock_status;
    668     }
    669   } [list main reserved temp $templockstate aux1 unlocked aux2 unlocked]
    670   do_test savepoint-10.2.4 {
    671     execsql {
    672       INSERT INTO t3 VALUES(3, 4);
    673       PRAGMA lock_status;
    674     }
    675   } [list main reserved temp $templockstate aux1 unlocked aux2 reserved]
    676   do_test savepoint-10.2.5 {
    677     execsql {
    678       SAVEPOINT two;
    679       INSERT INTO t2 VALUES(5, 6);
    680       PRAGMA lock_status;
    681     }
    682   } [list main reserved temp $templockstate aux1 reserved aux2 reserved]
    683   do_test savepoint-10.2.6 {
    684     execsql { SELECT * FROM t2 }
    685   } {5 6}
    686   do_test savepoint-10.2.7 {
    687     execsql { ROLLBACK TO two }
    688     execsql { SELECT * FROM t2 }
    689   } {}
    690   do_test savepoint-10.2.8 {
    691     execsql { PRAGMA lock_status }
    692   } [list main reserved temp $templockstate aux1 reserved aux2 reserved]
    693   do_test savepoint-10.2.9 {
    694     execsql { SELECT 'a', * FROM t1 UNION ALL SELECT 'b', * FROM t3 }
    695   } {a 1 2 b 3 4}
    696   do_test savepoint-10.2.9 {
    697     execsql {
    698       INSERT INTO t2 VALUES(5, 6);
    699       RELEASE one;
    700     }
    701     execsql { 
    702       SELECT * FROM t1;
    703       SELECT * FROM t2;
    704       SELECT * FROM t3;
    705     }
    706   } {1 2 5 6 3 4}
    707   do_test savepoint-10.2.9 {
    708     execsql { PRAGMA lock_status }
    709   } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
    710   
    711   do_test savepoint-10.2.10 {
    712     execsql { 
    713       SAVEPOINT one;
    714         INSERT INTO t1 VALUES('a', 'b');
    715         SAVEPOINT two;
    716           INSERT INTO t2 VALUES('c', 'd');
    717           SAVEPOINT three;
    718             INSERT INTO t3 VALUES('e', 'f');
    719     }
    720     execsql { 
    721       SELECT * FROM t1;
    722       SELECT * FROM t2;
    723       SELECT * FROM t3;
    724     }
    725   } {1 2 a b 5 6 c d 3 4 e f}
    726   do_test savepoint-10.2.11 {
    727     execsql { ROLLBACK TO two }
    728     execsql { 
    729       SELECT * FROM t1;
    730       SELECT * FROM t2;
    731       SELECT * FROM t3;
    732     }
    733   } {1 2 a b 5 6 3 4}
    734   do_test savepoint-10.2.12 {
    735     execsql { 
    736       INSERT INTO t3 VALUES('g', 'h');
    737       ROLLBACK TO two;
    738     }
    739     execsql { 
    740       SELECT * FROM t1;
    741       SELECT * FROM t2;
    742       SELECT * FROM t3;
    743     }
    744   } {1 2 a b 5 6 3 4}
    745   do_test savepoint-10.2.13 {
    746     execsql { ROLLBACK }
    747     execsql { 
    748       SELECT * FROM t1;
    749       SELECT * FROM t2;
    750       SELECT * FROM t3;
    751     }
    752   } {1 2 5 6 3 4}
    753   do_test savepoint-10.2.14 {
    754     execsql { PRAGMA lock_status }
    755   } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
    756 }
    757 
    758 #-------------------------------------------------------------------------
    759 # The following tests - savepoint-11.* - test the interaction of 
    760 # savepoints and creating or dropping tables and indexes in 
    761 # auto-vacuum mode.
    762 # 
    763 do_test savepoint-11.1 {
    764   db close
    765   file delete -force test.db
    766   sqlite3 db test.db
    767   execsql { PRAGMA auto_vacuum = full; }
    768   wal_set_journal_mode
    769   execsql {
    770     CREATE TABLE t1(a, b, UNIQUE(a, b));
    771     INSERT INTO t1 VALUES(1, randstr(1000,1000));
    772     INSERT INTO t1 VALUES(2, randstr(1000,1000));
    773   }
    774 } {}
    775 do_test savepoint-11.2 {
    776   execsql {
    777     SAVEPOINT one;
    778       CREATE TABLE t2(a, b, UNIQUE(a, b));
    779       SAVEPOINT two;
    780         CREATE TABLE t3(a, b, UNIQUE(a, b));
    781   }
    782 } {}
    783 integrity_check savepoint-11.3
    784 do_test savepoint-11.4 {
    785   execsql { ROLLBACK TO two }
    786 } {}
    787 integrity_check savepoint-11.5
    788 do_test savepoint-11.6 {
    789   execsql { 
    790     CREATE TABLE t3(a, b, UNIQUE(a, b));
    791     ROLLBACK TO one;
    792   }
    793 } {}
    794 integrity_check savepoint-11.7
    795 do_test savepoint-11.8 {
    796   execsql { ROLLBACK }
    797   execsql { PRAGMA wal_checkpoint }
    798   file size test.db
    799 } {8192}
    800 
    801 do_test savepoint-11.9 {
    802   execsql {
    803     DROP TABLE IF EXISTS t1;
    804     DROP TABLE IF EXISTS t2;
    805     DROP TABLE IF EXISTS t3;
    806   }
    807 } {}
    808 do_test savepoint-11.10 {
    809   execsql {
    810     BEGIN;
    811       CREATE TABLE t1(a, b);
    812       CREATE TABLE t2(x, y);
    813       INSERT INTO t2 VALUES(1, 2);
    814       SAVEPOINT one;
    815         INSERT INTO t2 VALUES(3, 4);
    816         SAVEPOINT two;
    817           DROP TABLE t1;
    818         ROLLBACK TO two;
    819   }
    820   execsql {SELECT * FROM t2}
    821 } {1 2 3 4}
    822 do_test savepoint-11.11 {
    823   execsql COMMIT
    824 } {}
    825 do_test savepoint-11.12 {
    826   execsql {SELECT * FROM t2}
    827 } {1 2 3 4}
    828 wal_check_journal_mode savepoint-11.13
    829 
    830 #-------------------------------------------------------------------------
    831 # The following tests - savepoint-12.* - test the interaction of 
    832 # savepoints and "ON CONFLICT ROLLBACK" clauses.
    833 # 
    834 do_test savepoint-12.1 {
    835   execsql {
    836     CREATE TABLE t4(a PRIMARY KEY, b);
    837     INSERT INTO t4 VALUES(1, 'one');
    838   }
    839 } {}
    840 do_test savepoint-12.2 {
    841   # The final statement of the following SQL hits a constraint when the
    842   # conflict handling mode is "OR ROLLBACK" and there are a couple of
    843   # open savepoints. At one point this would fail to clear the internal
    844   # record of the open savepoints, resulting in an assert() failure 
    845   # later on.
    846   # 
    847   catchsql {
    848     BEGIN;
    849       INSERT INTO t4 VALUES(2, 'two');
    850       SAVEPOINT sp1;
    851         INSERT INTO t4 VALUES(3, 'three');
    852         SAVEPOINT sp2;
    853           INSERT OR ROLLBACK INTO t4 VALUES(1, 'one');
    854   }
    855 } {1 {column a is not unique}}
    856 do_test savepoint-12.3 {
    857   sqlite3_get_autocommit db
    858 } {1}
    859 do_test savepoint-12.4 {
    860   execsql { SAVEPOINT one }
    861 } {}
    862 wal_check_journal_mode savepoint-12.5
    863 
    864 #-------------------------------------------------------------------------
    865 # The following tests - savepoint-13.* - test the interaction of 
    866 # savepoints and "journal_mode = off".
    867 # 
    868 if {[wal_is_wal_mode]==0} {
    869   do_test savepoint-13.1 {
    870     db close
    871     catch {file delete -force test.db}
    872     sqlite3 db test.db
    873     execsql {
    874       BEGIN;
    875         CREATE TABLE t1(a PRIMARY KEY, b);
    876         INSERT INTO t1 VALUES(1, 2);
    877       COMMIT;
    878       PRAGMA journal_mode = off;
    879     }
    880   } {off}
    881   do_test savepoint-13.2 {
    882     execsql {
    883       BEGIN;
    884       INSERT INTO t1 VALUES(3, 4);
    885       INSERT INTO t1 SELECT a+4,b+4  FROM t1;
    886       COMMIT;
    887     }
    888   } {}
    889   do_test savepoint-13.3 {
    890     execsql {
    891       BEGIN;
    892         INSERT INTO t1 VALUES(9, 10);
    893         SAVEPOINT s1;
    894           INSERT INTO t1 VALUES(11, 12);
    895       COMMIT;
    896     }
    897   } {}
    898   do_test savepoint-13.4 {
    899     execsql {
    900       BEGIN;
    901         INSERT INTO t1 VALUES(13, 14);
    902         SAVEPOINT s1;
    903           INSERT INTO t1 VALUES(15, 16);
    904         ROLLBACK TO s1;
    905       ROLLBACK;
    906       SELECT * FROM t1;
    907     }
    908   } {1 2 3 4 5 6 7 8 9 10 11 12}
    909 }
    910 
    911 db close
    912 file delete test.db
    913 do_multiclient_test tn {
    914   do_test savepoint-14.$tn.1 {
    915     sql1 {
    916       CREATE TABLE foo(x);
    917       INSERT INTO foo VALUES(1);
    918       INSERT INTO foo VALUES(2);
    919     }
    920     sql2 {
    921       BEGIN;
    922         SELECT * FROM foo;
    923     }
    924   } {1 2}
    925   do_test savepoint-14.$tn.2 {
    926     sql1 {
    927       SAVEPOINT one;
    928       INSERT INTO foo VALUES(1);
    929     }
    930     csql1 { RELEASE one }
    931   } {1 {database is locked}}
    932   do_test savepoint-14.$tn.3 {
    933     sql1 { ROLLBACK TO one }
    934     sql2 { COMMIT }
    935     sql1 { RELEASE one }
    936   } {}
    937 
    938   do_test savepoint-14.$tn.4 {
    939     sql2 {
    940       BEGIN;
    941         SELECT * FROM foo;
    942     }
    943   } {1 2}
    944   do_test savepoint-14.$tn.5 {
    945     sql1 {
    946       SAVEPOINT one;
    947       INSERT INTO foo VALUES(1);
    948     }
    949     csql1 { RELEASE one }
    950   } {1 {database is locked}}
    951   do_test savepoint-14.$tn.6 {
    952     sql2 { COMMIT }
    953     sql1 {
    954       ROLLBACK TO one;
    955       INSERT INTO foo VALUES(3);
    956       INSERT INTO foo VALUES(4);
    957       INSERT INTO foo VALUES(5);
    958       RELEASE one;
    959     }
    960   } {}
    961   do_test savepoint-14.$tn.7 {
    962     sql2 { CREATE INDEX fooidx ON foo(x); }
    963     sql3 { PRAGMA integrity_check }
    964   } {ok}
    965 }
    966 
    967 do_multiclient_test tn {
    968   do_test savepoint-15.$tn.1 {
    969     sql1 {
    970       CREATE TABLE foo(x);
    971       INSERT INTO foo VALUES(1);
    972       INSERT INTO foo VALUES(2);
    973     }
    974     sql2 { BEGIN; SELECT * FROM foo; }
    975   } {1 2}
    976   do_test savepoint-15.$tn.2 {
    977     sql1 {
    978       PRAGMA locking_mode = EXCLUSIVE;
    979       BEGIN;
    980         INSERT INTO foo VALUES(3);
    981     }
    982     csql1 { COMMIT }
    983   } {1 {database is locked}}
    984   do_test savepoint-15.$tn.3 {
    985     sql1 { ROLLBACK }
    986     sql2 { COMMIT }
    987     sql1 {
    988       INSERT INTO foo VALUES(3);
    989       PRAGMA locking_mode = NORMAL;
    990       INSERT INTO foo VALUES(4);
    991     }
    992     sql2 { CREATE INDEX fooidx ON foo(x); }
    993     sql3 { PRAGMA integrity_check }
    994   } {ok}
    995 }
    996 
    997 do_multiclient_test tn {
    998   do_test savepoint-16.$tn.1 {
    999     sql1 {
   1000       CREATE TABLE foo(x);
   1001       INSERT INTO foo VALUES(1);
   1002       INSERT INTO foo VALUES(2);
   1003     }
   1004   } {}
   1005   do_test savepoint-16.$tn.2 {
   1006 
   1007     db eval {SELECT * FROM foo} {
   1008       sql1 { INSERT INTO foo VALUES(3) }
   1009       sql2 { SELECT * FROM foo }
   1010       sql1 { INSERT INTO foo VALUES(4) }
   1011       break
   1012     }
   1013 
   1014     sql2 { CREATE INDEX fooidx ON foo(x); }
   1015     sql3 { PRAGMA integrity_check }
   1016   } {ok}
   1017   do_test savepoint-16.$tn.3 {
   1018     sql1 { SELECT * FROM foo }
   1019   } {1 2 3 4}
   1020 }
   1021 
   1022 #-------------------------------------------------------------------------
   1023 # This next block of tests verifies that a problem reported on the mailing
   1024 # list has been resolved. At one point the second "CREATE TABLE t6" would
   1025 # fail as table t6 still existed in the internal cache of the db schema
   1026 # (even though it had been removed from the database by the ROLLBACK 
   1027 # command).
   1028 #
   1029 sqlite3 db test.db
   1030 do_execsql_test savepoint-17.1 {
   1031   BEGIN;
   1032     CREATE TABLE t6(a, b);
   1033     INSERT INTO t6 VALUES(1, 2);
   1034     SAVEPOINT one;
   1035       INSERT INTO t6 VALUES(3, 4);
   1036     ROLLBACK TO one;
   1037     SELECT * FROM t6;
   1038   ROLLBACK;
   1039 } {1 2}
   1040 
   1041 do_execsql_test savepoint-17.2 {
   1042   CREATE TABLE t6(a, b);
   1043 } {}
   1044 
   1045 finish_test
   1046