github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/notify1.test (about)

     1  # 2009 March 04
     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 SQLite library.  The
    12  # focus of this file is testing the sqlite3_unlock_notify() API.
    13  #
    14  # $Id: notify1.test,v 1.4 2009/06/05 17:09:12 drh Exp $
    15  
    16  set testdir [file dirname $argv0]
    17  source $testdir/tester.tcl
    18  
    19  ifcapable !unlock_notify||!shared_cache {
    20    finish_test
    21    return
    22  }
    23  db close
    24  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
    25  
    26  #-------------------------------------------------------------------------
    27  # Warm body test. Test that an unlock-notify callback can be registered 
    28  # and that it is invoked.
    29  #
    30  do_test notify1-1.1 {
    31    sqlite3 db test.db
    32    sqlite3 db2 test.db
    33    execsql { CREATE TABLE t1(a, b) }
    34  } {}
    35  do_test notify1-1.2 {
    36    execsql {
    37      BEGIN;
    38      INSERT INTO t1 VALUES(1, 2);
    39    }
    40    catchsql { INSERT INTO t1 VALUES(3, 4) } db2
    41  } {1 {database table is locked}}
    42  do_test notify1-1.3 {
    43    set zScript ""
    44    db2 unlock_notify {
    45      set zScript "db2 eval { INSERT INTO t1 VALUES(3, 4) }"
    46    }
    47    execsql { SELECT * FROM t1 }
    48  } {1 2}
    49  do_test notify1-1.4 {
    50    set zScript
    51  } {}
    52  do_test notify1-1.5 {
    53    execsql { COMMIT }
    54    eval $zScript
    55    execsql { SELECT * FROM t1 }
    56  } {1 2 3 4}
    57  
    58  #-------------------------------------------------------------------------
    59  # Verify that invoking the "unlock_notify" method with no arguments
    60  # (which is the equivalent of invoking sqlite3_unlock_notify() with
    61  # a NULL xNotify argument) cancels a pending notify callback.
    62  #
    63  do_test notify1-1.11 {
    64    execsql { DROP TABLE t1; CREATE TABLE t1(a, b) }
    65  } {}
    66  do_test notify1-1.12 {
    67    execsql {
    68      BEGIN;
    69      INSERT INTO t1 VALUES(1, 2);
    70    }
    71    catchsql { INSERT INTO t1 VALUES(3, 4) } db2
    72  } {1 {database table is locked}}
    73  do_test notify1-1.13 {
    74    set zScript ""
    75    db2 unlock_notify {
    76      set zScript "db2 eval { INSERT INTO t1 VALUES(3, 4) }"
    77    }
    78    execsql { SELECT * FROM t1 }
    79  } {1 2}
    80  do_test notify1-1.14 {
    81    set zScript
    82  } {}
    83  do_test notify1-1.15 {
    84    db2 unlock_notify
    85    execsql { COMMIT }
    86    eval $zScript
    87    execsql { SELECT * FROM t1 }
    88  } {1 2}
    89  
    90  #-------------------------------------------------------------------------
    91  # The following tests, notify1-2.*, test that deadlock is detected 
    92  # correctly.
    93  # 
    94  do_test notify1-2.1 {
    95    execsql { 
    96      CREATE TABLE t2(a, b);
    97      INSERT INTO t2 VALUES('I', 'II');
    98    }
    99  } {}
   100  
   101  #
   102  # Test for simple deadlock involving two database connections.
   103  #
   104  # 1. Grab a write-lock on t1 with [db]. Then grab a read-lock on t2 with [db2].
   105  # 2. Try to grab a read-lock on t1 with [db2] (fails).
   106  # 3. Have [db2] wait on the read-lock it failed to obtain in step 2.
   107  # 4. Try to grab a write-lock on t2 with [db] (fails).
   108  # 5. Try to have [db] wait on the lock from step 4. Fails, as the system
   109  #    would be deadlocked (since [db2] is already waiting on [db], and this
   110  #    operation would have [db] wait on [db2]).
   111  #
   112  do_test notify1-2.2.1 {
   113    execsql {
   114      BEGIN;
   115      INSERT INTO t1 VALUES(5, 6);
   116    }
   117    execsql {
   118      BEGIN;
   119      SELECT * FROM t2;
   120    } db2
   121  } {I II}
   122  do_test notify1-2.2.2 {
   123    catchsql { SELECT * FROM t1 } db2
   124  } {1 {database table is locked: t1}}
   125  do_test notify1-2.2.3 {
   126    db2 unlock_notify {lappend unlock_notify db2}
   127  } {}
   128  do_test notify1-2.2.4 {
   129    catchsql { INSERT INTO t2 VALUES('III', 'IV') }
   130  } {1 {database table is locked: t2}}
   131  do_test notify1-2.2.5 {
   132    set rc [catch { db unlock_notify {lappend unlock_notify db} } msg]
   133    list $rc $msg
   134  } {1 {database is deadlocked}}
   135  
   136  #
   137  # Test for slightly more complex deadlock involving three database
   138  # connections: db, db2 and db3.
   139  #
   140  do_test notify1-2.3.1 {
   141    db close
   142    db2 close
   143    forcedelete test.db test2.db test3.db
   144    foreach con {db db2 db3} {
   145      sqlite3 $con test.db
   146      $con eval { ATTACH 'test2.db' AS aux2 }
   147      $con eval { ATTACH 'test3.db' AS aux3 }
   148    }
   149    execsql {
   150      CREATE TABLE main.t1(a, b);
   151      CREATE TABLE aux2.t2(a, b);
   152      CREATE TABLE aux3.t3(a, b);
   153    }
   154  } {}
   155  do_test notify1-2.3.2 {
   156    execsql { BEGIN ; INSERT INTO t1 VALUES(1, 2) } db
   157    execsql { BEGIN ; INSERT INTO t2 VALUES(1, 2) } db2
   158    execsql { BEGIN ; INSERT INTO t3 VALUES(1, 2) } db3
   159  } {}
   160  do_test notify1-2.3.3 {
   161    catchsql { SELECT * FROM t2 } db
   162  } {1 {database table is locked: t2}}
   163  do_test notify1-2.3.4 {
   164    catchsql { SELECT * FROM t3 } db2
   165  } {1 {database table is locked: t3}}
   166  do_test notify1-2.3.5 {
   167    catchsql { SELECT * FROM t1 } db3
   168  } {1 {database table is locked: t1}}
   169  do_test notify1-2.3.6 {
   170    set lUnlock [list]
   171    db  unlock_notify {lappend lUnlock db}
   172    db2 unlock_notify {lappend lUnlock db2}
   173  } {}
   174  do_test notify1-2.3.7 {
   175    set rc [catch { db3 unlock_notify {lappend lUnlock db3} } msg]
   176    list $rc $msg
   177  } {1 {database is deadlocked}}
   178  do_test notify1-2.3.8 {
   179    execsql { COMMIT }
   180    set lUnlock
   181  } {}
   182  do_test notify1-2.3.9 {
   183    db3 unlock_notify {lappend lUnlock db3} 
   184    set lUnlock
   185  } {db3}
   186  do_test notify1-2.3.10 {
   187    execsql { COMMIT } db2
   188    set lUnlock
   189  } {db3 db}
   190  do_test notify1-2.3.11 {
   191    execsql { COMMIT } db3
   192    set lUnlock
   193  } {db3 db db2}
   194  catch { db3 close }
   195  catch { db2 close }
   196  catch { db close }
   197  
   198  #-------------------------------------------------------------------------
   199  # The following tests, notify1-3.* and notify1-4.*, test that callbacks 
   200  # can be issued when there are many (>16) connections waiting on a single 
   201  # unlock event.
   202  # 
   203  foreach {tn nConn} {3 20 4 76} {
   204    do_test notify1-$tn.1 {
   205      sqlite3 db test.db
   206      execsql {
   207        BEGIN;
   208        INSERT INTO t1 VALUES('a', 'b');
   209      }
   210    } {}
   211    set lUnlock [list]
   212    set lUnlockFinal [list]
   213    for {set ii 1} {$ii <= $nConn} {incr ii} {
   214      do_test notify1-$tn.2.$ii.1 {
   215        set cmd "db$ii"
   216        sqlite3 $cmd test.db
   217        catchsql { SELECT * FROM t1 } $cmd
   218      } {1 {database table is locked: t1}}
   219      do_test notify1-$tn.2.$ii.2 {
   220        $cmd unlock_notify "lappend lUnlock $ii"
   221      } {}
   222      lappend lUnlockFinal $ii
   223    }
   224    do_test notify1-$tn.3 {
   225      set lUnlock
   226    } {}
   227    do_test notify1-$tn.4 {
   228      execsql {COMMIT}
   229      lsort -integer $lUnlock
   230    } $lUnlockFinal
   231    do_test notify1-$tn.5 {
   232      for {set ii 1} {$ii <= $nConn} {incr ii} {
   233        "db$ii" close
   234      }
   235    } {}
   236  }
   237  db close
   238  
   239  #-------------------------------------------------------------------------
   240  # These tests, notify1-5.*, test that a malloc() failure that occurs while
   241  # allocating an array to use as an argument to an unlock-notify callback
   242  # is handled correctly.
   243  # 
   244  source $testdir/malloc_common.tcl
   245  do_malloc_test notify1-5 -tclprep {
   246    set ::lUnlock [list]
   247    execsql {
   248      CREATE TABLE t1(a, b);
   249      BEGIN;
   250      INSERT INTO t1 VALUES('a', 'b');
   251    }
   252    for {set ii 1} {$ii <= 60} {incr ii} {
   253      set cmd "db$ii"
   254      sqlite3 $cmd test.db
   255      catchsql { SELECT * FROM t1 } $cmd
   256      $cmd unlock_notify "lappend ::lUnlock $ii"
   257    }
   258  } -sqlbody {
   259    COMMIT;
   260  } -cleanup {
   261    # One of two things should have happened:
   262    #
   263    #   1) The transaction opened by [db] was not committed. No unlock-notify
   264    #      callbacks were invoked, OR
   265    #   2) The transaction opened by [db] was committed and 60 unlock-notify
   266    #      callbacks were invoked.
   267    #
   268    do_test notify1-5.systemstate {
   269      expr { ([llength $::lUnlock]==0 && [sqlite3_get_autocommit db]==0)
   270          || ([llength $::lUnlock]==60 && [sqlite3_get_autocommit db]==1)
   271      }
   272    } {1}
   273    for {set ii 1} {$ii <= 60} {incr ii} { "db$ii" close }
   274  }
   275  
   276  #-------------------------------------------------------------------------
   277  # Test cases notify1-6.* test cases where the following occur:
   278  # 
   279  #   notify1-6.1.*: Test encountering an SQLITE_LOCKED error when the
   280  #                  "blocking connection" has already been set by a previous
   281  #                  SQLITE_LOCKED.
   282  #
   283  #   notify1-6.2.*: Test encountering an SQLITE_LOCKED error when already
   284  #                  waiting on an unlock-notify callback.
   285  #
   286  #   notify1-6.3.*: Test that if an SQLITE_LOCKED error is encountered while
   287  #                  already waiting on an unlock-notify callback, and then
   288  #                  the blocker that caused the SQLITE_LOCKED commits its
   289  #                  transaction, the unlock-notify callback is not invoked.
   290  #
   291  #   notify1-6.4.*: Like 6.3.*, except that instead of the second blocker
   292  #                  committing its transaction, the first does. The 
   293  #                  unlock-notify callback is therefore invoked.
   294  #
   295  db close
   296  do_test notify1-6.1.1 {
   297    forcedelete test.db test2.db
   298    foreach conn {db db2 db3} {
   299      sqlite3 $conn test.db
   300      execsql { ATTACH 'test2.db' AS two } $conn
   301    }
   302    execsql {
   303      CREATE TABLE t1(a, b);
   304      CREATE TABLE two.t2(a, b);
   305    }
   306    execsql { 
   307      BEGIN;
   308      INSERT INTO t1 VALUES(1, 2);
   309    } db2
   310    execsql { 
   311      BEGIN;
   312      INSERT INTO t2 VALUES(1, 2);
   313    } db3
   314  } {}
   315  do_test notify1-6.1.2 {
   316    catchsql { SELECT * FROM t2 }
   317  } {1 {database table is locked: t2}}
   318  do_test notify1-6.1.3 {
   319    catchsql { SELECT * FROM t1 }
   320  } {1 {database table is locked: t1}}
   321  
   322  do_test notify1-6.2.1 {
   323    set unlocked 0
   324    db unlock_notify {set unlocked 1}
   325    set unlocked
   326  } {0}
   327  do_test notify1-6.2.2 {
   328    catchsql { SELECT * FROM t2 }
   329  } {1 {database table is locked: t2}}
   330  do_test notify1-6.2.3 {
   331    execsql { COMMIT } db2
   332    set unlocked
   333  } {1}
   334  
   335  do_test notify1-6.3.1 {
   336    execsql { 
   337      BEGIN;
   338      INSERT INTO t1 VALUES(3, 4);
   339    } db2
   340  } {}
   341  do_test notify1-6.3.2 {
   342    catchsql { SELECT * FROM t1 }
   343  } {1 {database table is locked: t1}}
   344  do_test notify1-6.3.3 {
   345    set unlocked 0
   346    db unlock_notify {set unlocked 1}
   347    set unlocked
   348  } {0}
   349  do_test notify1-6.3.4 {
   350    catchsql { SELECT * FROM t2 }
   351  } {1 {database table is locked: t2}}
   352  do_test notify1-6.3.5 {
   353    execsql { COMMIT } db3
   354    set unlocked
   355  } {0}
   356  
   357  do_test notify1-6.4.1 {
   358    execsql { 
   359      BEGIN;
   360      INSERT INTO t2 VALUES(3, 4);
   361    } db3
   362    catchsql { SELECT * FROM t2 }
   363  } {1 {database table is locked: t2}}
   364  do_test notify1-6.4.2 {
   365    execsql { COMMIT } db2
   366    set unlocked
   367  } {1}
   368  do_test notify1-6.4.3 {
   369    execsql { COMMIT } db3
   370  } {}
   371  db close
   372  db2 close
   373  db3 close
   374  
   375  #-------------------------------------------------------------------------
   376  # Test cases notify1-7.* tests that when more than one distinct 
   377  # unlock-notify function is registered, all are invoked correctly.
   378  #
   379  proc unlock_notify {} {
   380    incr ::unlock_notify
   381  }
   382  do_test notify1-7.1 {
   383    foreach conn {db db2 db3} {
   384      sqlite3 $conn test.db
   385    }
   386    execsql {
   387      BEGIN;
   388      INSERT INTO t1 VALUES(5, 6);
   389    }
   390  } {}
   391  do_test notify1-7.2 {
   392    catchsql { SELECT * FROM t1 } db2
   393  } {1 {database table is locked: t1}}
   394  do_test notify1-7.3 {
   395    catchsql { SELECT * FROM t1 } db3
   396  } {1 {database table is locked: t1}}
   397  do_test notify1-7.4 {
   398    set unlock_notify 0
   399    db2 unlock_notify unlock_notify
   400    sqlite3_unlock_notify db3
   401  } {SQLITE_OK}
   402  do_test notify1-7.5 {
   403    set unlock_notify
   404  } {0}
   405  do_test notify1-7.6 {
   406    execsql { COMMIT }
   407    set unlock_notify
   408  } {2}
   409  
   410  #-------------------------------------------------------------------------
   411  # Test cases notify1-8.* tests that the correct SQLITE_LOCKED extended 
   412  # error code is returned in various scenarios.
   413  #
   414  do_test notify1-8.1 {
   415    execsql {
   416      BEGIN;
   417      INSERT INTO t1 VALUES(7, 8);
   418    }
   419    catchsql { SELECT * FROM t1 } db2
   420  } {1 {database table is locked: t1}}
   421  do_test notify1-8.2 {
   422    sqlite3_extended_errcode db2
   423  } {SQLITE_LOCKED_SHAREDCACHE}
   424  
   425  do_test notify1-8.3 {
   426    execsql {
   427      COMMIT;
   428      BEGIN EXCLUSIVE;
   429    }
   430    catchsql { SELECT * FROM t1 } db2
   431  } {1 {database schema is locked: main}}
   432  do_test notify1-8.4 {
   433    sqlite3_extended_errcode db2
   434  } {SQLITE_LOCKED_SHAREDCACHE}
   435  
   436  do_test notify1-8.X {
   437    execsql { COMMIT } 
   438  } {}
   439  
   440  #-------------------------------------------------------------------------
   441  # Test cases notify1-9.* test the shared-cache 'pending-lock' feature.
   442  #
   443  do_test notify1-9.1 {
   444    execsql {
   445      CREATE TABLE t2(a, b);
   446      BEGIN;
   447      SELECT * FROM t1;
   448    } db2
   449  } {1 2 3 4 5 6 7 8}
   450  do_test notify1-9.2 {
   451    execsql { SELECT * FROM t1 } db3
   452  } {1 2 3 4 5 6 7 8}
   453  do_test notify1-9.3 {
   454    catchsql { 
   455      BEGIN;
   456      INSERT INTO t1 VALUES(9, 10);
   457    }
   458  } {1 {database table is locked: t1}}
   459  do_test notify1-9.4 {
   460    catchsql { SELECT * FROM t2 } db3
   461  } {1 {database table is locked}}
   462  do_test notify1-9.5 {
   463    execsql  { COMMIT } db2
   464    execsql { SELECT * FROM t2 } db3
   465  } {}
   466  do_test notify1-9.6 {
   467    execsql  { COMMIT }
   468  } {}
   469  
   470  do_test notify1-9.7 {
   471    execsql {
   472      BEGIN;
   473      SELECT * FROM t1;
   474    } db2
   475  } {1 2 3 4 5 6 7 8}
   476  do_test notify1-9.8 {
   477    execsql { SELECT * FROM t1 } db3
   478  } {1 2 3 4 5 6 7 8}
   479  do_test notify1-9.9 {
   480    catchsql { 
   481      BEGIN;
   482      INSERT INTO t1 VALUES(9, 10);
   483    }
   484  } {1 {database table is locked: t1}}
   485  do_test notify1-9.10 {
   486    catchsql { SELECT * FROM t2 } db3
   487  } {1 {database table is locked}}
   488  do_test notify1-9.11 {
   489    execsql  { COMMIT }
   490    execsql { SELECT * FROM t2 } db3
   491  } {}
   492  do_test notify1-9.12 {
   493    execsql  { COMMIT } db2
   494  } {}
   495  
   496  db close
   497  db2 close
   498  db3 close
   499  sqlite3_enable_shared_cache $::enable_shared_cache
   500  finish_test