modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/session/sessionfault.test (about)

     1  # 2011 Mar 21
     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  # The focus of this file is testing the session module.
    13  #
    14  
    15  if {![info exists testdir]} {
    16    set testdir [file join [file dirname [info script]] .. .. test]
    17  } 
    18  source [file join [file dirname [info script]] session_common.tcl]
    19  source $testdir/tester.tcl
    20  ifcapable !session {finish_test; return}
    21  
    22  set testprefix sessionfault
    23  
    24  forcedelete test.db2
    25  sqlite3 db2 test.db2
    26  do_common_sql {
    27    CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
    28    INSERT INTO t1 VALUES(1, 2, 3);
    29    INSERT INTO t1 VALUES(4, 5, 6);
    30  }
    31  faultsim_save_and_close
    32  db2 close
    33  
    34  #-------------------------------------------------------------------------
    35  # Test OOM error handling when collecting and applying a simple changeset.
    36  #
    37  # Test 1.1 attaches tables individually by name to the session object. 
    38  # Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
    39  # tables.
    40  #
    41  do_faultsim_test 1.1 -faults oom-* -prep {
    42    catch {db2 close}
    43    catch {db close}
    44    faultsim_restore_and_reopen
    45    sqlite3 db2 test.db2
    46  } -body {
    47    do_then_apply_sql {
    48      INSERT INTO t1 VALUES('a string value', 8, 9);
    49      UPDATE t1 SET c = 10 WHERE a = 1;
    50      DELETE FROM t1 WHERE a = 4;
    51    }
    52  } -test {
    53    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
    54    faultsim_integrity_check
    55    if {$testrc==0} { compare_db db db2 }
    56  }
    57  
    58  do_faultsim_test 1.2 -faults oom-* -prep {
    59    catch {db2 close}
    60    catch {db close}
    61    faultsim_restore_and_reopen
    62  } -body {
    63    sqlite3session S db main
    64    S attach *
    65    execsql {
    66      INSERT INTO t1 VALUES('a string value', 8, 9);
    67      UPDATE t1 SET c = 10 WHERE a = 1;
    68      DELETE FROM t1 WHERE a = 4;
    69    }
    70    set ::changeset [S changeset]
    71    set {} {}
    72  } -test {
    73    catch { S delete }
    74    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
    75    faultsim_integrity_check
    76    if {$testrc==0} { 
    77      proc xConflict {args} { return "OMIT" }
    78      sqlite3 db2 test.db2
    79      sqlite3changeset_apply db2 $::changeset xConflict
    80      compare_db db db2 
    81    }
    82  }
    83  
    84  #-------------------------------------------------------------------------
    85  # The following block of tests - 2.* - are designed to check 
    86  # the handling of faults in the sqlite3changeset_apply() function.
    87  #
    88  catch {db close}
    89  catch {db2 close}
    90  forcedelete test.db2 test.db
    91  sqlite3 db2 test.db2
    92  sqlite3 db test.db
    93  do_common_sql {
    94    CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
    95    INSERT INTO t1 VALUES('apple', 'orange', 'pear');
    96  
    97    CREATE TABLE t2(x PRIMARY KEY, y);
    98  }
    99  db2 close
   100  faultsim_save_and_close
   101  
   102  
   103  foreach {tn conflict_policy sql sql2} {
   104    1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
   105    2 OMIT { DELETE FROM t1 WHERE a = 'apple' }                         {}
   106    3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' }            {}
   107    4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
   108      INSERT INTO t2 VALUES('keyvalue', 'value 2');
   109    }
   110  } {
   111    proc xConflict args [list return $conflict_policy]
   112  
   113    do_faultsim_test 2.$tn -faults oom-transient -prep {
   114      catch {db2 close}
   115      catch {db close}
   116      faultsim_restore_and_reopen
   117      set ::changeset [changeset_from_sql $::sql]
   118      sqlite3 db2 test.db2
   119      sqlite3_db_config_lookaside db2 0 0 0
   120      execsql $::sql2 db2
   121    } -body {
   122      sqlite3changeset_apply db2 $::changeset xConflict
   123    } -test {
   124      faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   125      faultsim_integrity_check
   126      if {$testrc==0} { compare_db db db2 }
   127    }
   128  }
   129  
   130  #-------------------------------------------------------------------------
   131  # This test case is designed so that a malloc() failure occurs while
   132  # resizing the session object hash-table from 256 to 512 buckets. This
   133  # is not an error, just a sub-optimal condition.
   134  #
   135  do_faultsim_test 3 -faults oom-* -prep {
   136    catch {db2 close}
   137    catch {db close}
   138    faultsim_restore_and_reopen
   139    sqlite3 db2 test.db2
   140  
   141    sqlite3session S db main
   142    S attach t1
   143    execsql { BEGIN }
   144    for {set i 0} {$i < 125} {incr i} {
   145      execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
   146    }
   147  } -body {
   148    for {set i 125} {$i < 133} {incr i} {
   149      execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
   150    }
   151    S changeset
   152    set {} {}
   153  } -test {
   154    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   155    if {$testrc==0} { 
   156      sqlite3changeset_apply db2 [S changeset] xConflict
   157      compare_db db db2 
   158    }
   159    catch { S delete }
   160    faultsim_integrity_check
   161  }
   162  
   163  catch { db close }
   164  catch { db2 close }
   165  forcedelete test.db2 test.db
   166  sqlite3 db2 test.db2
   167  sqlite3 db test.db
   168  
   169  proc xConflict {op tbl type args} {
   170    if { $type=="CONFLICT" || $type=="DATA" } {
   171      return "REPLACE"
   172    }
   173    return "OMIT"
   174  }
   175  
   176  do_test 4.0 {
   177    execsql {
   178      PRAGMA encoding = 'utf16';
   179      CREATE TABLE t1(a PRIMARY KEY, b);
   180      INSERT INTO t1 VALUES(5, 32);
   181    }
   182    execsql {
   183      PRAGMA encoding = 'utf16';
   184      CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
   185      INSERT INTO t1 VALUES(1, 2);
   186      INSERT INTO t1 VALUES(2, 4);
   187      INSERT INTO t1 VALUES(4, 16);
   188    } db2
   189  } {}
   190  
   191  faultsim_save_and_close
   192  db2 close
   193  
   194  do_faultsim_test 4 -faults oom-* -prep {
   195    catch {db2 close}
   196    catch {db close}
   197    faultsim_restore_and_reopen
   198    sqlite3 db2 test.db2
   199    sqlite3session S db main
   200    S attach t1
   201    execsql {
   202      INSERT INTO t1 VALUES(1, 45);
   203      INSERT INTO t1 VALUES(2, 55);
   204      INSERT INTO t1 VALUES(3, 55);
   205      UPDATE t1 SET a = 4 WHERE a = 5;
   206    }
   207  } -body {
   208    sqlite3changeset_apply db2 [S changeset] xConflict
   209  } -test {
   210    catch { S delete }
   211    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   212    if {$testrc==0} { compare_db db db2 }
   213  }
   214  
   215  #-------------------------------------------------------------------------
   216  # This block of tests verifies that OOM faults in the 
   217  # sqlite3changeset_invert() function are handled correctly.
   218  #
   219  catch {db close}
   220  catch {db2 close}
   221  forcedelete test.db
   222  sqlite3 db test.db
   223  execsql {
   224    CREATE TABLE t1(a, b, PRIMARY KEY(b));
   225    CREATE TABLE t2(a PRIMARY KEY, b);
   226    INSERT INTO t1 VALUES('string', 1);
   227    INSERT INTO t1 VALUES(4, 2);
   228    INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
   229  }
   230  set changeset [changeset_from_sql {
   231    INSERT INTO t1 VALUES('xxx', 'yyy');
   232    DELETE FROM t1 WHERE a = 'string';
   233    UPDATE t1 SET a = 20 WHERE b = 2;
   234  }]
   235  db close
   236  
   237  do_faultsim_test 5.1 -faults oom* -body {
   238    set ::inverse [sqlite3changeset_invert $::changeset]
   239    set {} {}
   240  } -test {
   241    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   242    if {$testrc==0} {
   243      set x [list]
   244      sqlite3session_foreach c $::inverse { lappend x $c }
   245      foreach c {
   246          {DELETE t1 0 .X {t xxx t yyy} {}} 
   247          {INSERT t1 0 .X {} {t string i 1}} 
   248          {UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}}
   249      } { lappend y $c }
   250      if {$x != $y} { error "changeset no good" }
   251    }
   252  }
   253  
   254  catch {db close}
   255  catch {db2 close}
   256  forcedelete test.db
   257  sqlite3 db test.db
   258  execsql {
   259    CREATE TABLE t2(a PRIMARY KEY, b);
   260    INSERT INTO t2 VALUES(1, 'abc');
   261    INSERT INTO t2 VALUES(2, 'def');
   262  }
   263  set changeset [changeset_from_sql {
   264    UPDATE t2 SET b = (b || b || b || b);
   265    UPDATE t2 SET b = (b || b || b || b);
   266    UPDATE t2 SET b = (b || b || b || b);
   267    UPDATE t2 SET b = (b || b || b || b);
   268  }]
   269  db close
   270  set abc [string repeat abc 256]
   271  set def [string repeat def 256]
   272  
   273  do_faultsim_test 5.2 -faults oom-tra* -body {
   274    set ::inverse [sqlite3changeset_invert $::changeset]
   275    set {} {}
   276  } -test {
   277    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   278    if {$testrc==0} {
   279      set x [list]
   280      sqlite3session_foreach c $::inverse { lappend x $c }
   281      foreach c "
   282          {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
   283          {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
   284      " { lappend y $c }
   285      if {$x != $y} { error "changeset no good" }
   286    }
   287  }
   288  
   289  catch {db close}
   290  catch {db2 close}
   291  forcedelete test.db
   292  sqlite3 db test.db
   293  set abc [string repeat abc 256]
   294  set def [string repeat def 256]
   295  execsql "
   296    CREATE TABLE t2(a PRIMARY KEY, b);
   297    INSERT INTO t2 VALUES(1, '$abc');
   298  "
   299  set changeset [changeset_from_sql "
   300    INSERT INTO t2 VALUES(2, '$def');
   301    DELETE FROM t2 WHERE a = 1;
   302  "]
   303  db close
   304  
   305  do_faultsim_test 5.3 -faults oom-tra* -body {
   306    set ::inverse [sqlite3changeset_invert $::changeset]
   307    set {} {}
   308  } -test {
   309    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   310    if {$testrc==0} {
   311      set x [list]
   312      sqlite3session_foreach c $::inverse { lappend x $c }
   313      foreach c "
   314          {INSERT t2 0 X. {} {i 1 t $::abc}}
   315          {DELETE t2 0 X. {i 2 t $::def} {}}
   316      " { lappend y $c }
   317      if {$x != $y} { error "changeset no good" }
   318    }
   319  }
   320  
   321  #-------------------------------------------------------------------------
   322  # Test that OOM errors in sqlite3changeset_concat() are handled correctly.
   323  #
   324  catch {db close}
   325  forcedelete test.db
   326  sqlite3 db test.db
   327  do_execsql_test 5.prep1 {
   328    CREATE TABLE t1(a, b, PRIMARY KEY(b));
   329    CREATE TABLE t2(a PRIMARY KEY, b);
   330    INSERT INTO t1 VALUES('string', 1);
   331    INSERT INTO t1 VALUES(4, 2);
   332    INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
   333  }
   334  
   335  do_test 6.prep2 {
   336    sqlite3session M db main
   337    M attach *
   338    set ::c2 [changeset_from_sql {
   339      INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
   340      INSERT INTO t2 VALUES('one', 'two');
   341      INSERT INTO t2 VALUES(1, NULL);
   342      UPDATE t1 SET a = 5 WHERE a = 2;
   343    }]
   344    set ::c1 [changeset_from_sql {
   345      DELETE FROM t2 WHERE a = 1;
   346      UPDATE t1 SET a = 4 WHERE a = 2;
   347      INSERT INTO t2 VALUES('x', 'y');
   348    }]
   349    set ::total [changeset_to_list [M changeset]]
   350    M delete
   351  } {}
   352  
   353  do_faultsim_test 6 -faults oom-* -body {
   354    set ::result [sqlite3changeset_concat $::c1 $::c2]
   355    set {} {}
   356  } -test {
   357    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   358    if {$testrc==0} {
   359      set v [changeset_to_list $::result]
   360      if {$v != $::total} { error "result no good" }
   361    }
   362  }
   363  
   364  faultsim_delete_and_reopen
   365  do_execsql_test 7.prep1 {
   366    CREATE TABLE t1(a, b, PRIMARY KEY(a));
   367  }
   368  faultsim_save_and_close
   369  
   370  set res [list]
   371  for {set ::i 0} {$::i < 480} {incr ::i 4} {
   372    lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
   373  }
   374  set res [lsort $res]
   375  do_faultsim_test 7 -faults oom-transient -prep {
   376    catch { S delete }
   377    faultsim_restore_and_reopen
   378    sqlite3session S db main
   379    S attach *
   380  } -body {
   381    for {set ::i 0} {$::i < 480} {incr ::i 4} {
   382      execsql {INSERT INTO t1 VALUES($::i, $::i)}
   383    }
   384  } -test {
   385    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   386    if {$testrc==0} {
   387      set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
   388      S delete
   389      if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
   390        error "Expected {0 $::res} Got {$cres}"
   391      }
   392    } else {
   393      catch { S changeset }
   394      catch { S delete }
   395    }
   396  }
   397  
   398  faultsim_delete_and_reopen
   399  do_test 8.prep {
   400    sqlite3session S db main
   401    S attach *
   402    execsql { 
   403      CREATE TABLE t1(a, b, PRIMARY KEY(a)); 
   404      INSERT INTO t1 VALUES(1, 2);
   405      INSERT INTO t1 VALUES(3, 4);
   406      INSERT INTO t1 VALUES(5, 6);
   407    }
   408    set ::changeset [S changeset]
   409    S delete
   410  } {}
   411  
   412  set expected [normalize_list {
   413    {INSERT t1 0 X. {} {i 1 i 2}} 
   414    {INSERT t1 0 X. {} {i 3 i 4}} 
   415    {INSERT t1 0 X. {} {i 5 i 6}}
   416  }]
   417  do_faultsim_test 8.1 -faults oom* -body {
   418    set ::res [list]
   419    sqlite3session_foreach -next v $::changeset { lappend ::res $v }
   420    normalize_list $::res
   421  } -test {
   422    faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
   423  }
   424  do_faultsim_test 8.2 -faults oom* -body {
   425    set ::res [list]
   426    sqlite3session_foreach v $::changeset { lappend ::res $v }
   427    normalize_list $::res
   428  } -test {
   429    faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
   430  }
   431  
   432  faultsim_delete_and_reopen
   433  do_test 9.1.prep {
   434    execsql { 
   435      PRAGMA encoding = 'utf16';
   436      CREATE TABLE t1(a PRIMARY KEY, b);
   437    }
   438  } {}
   439  faultsim_save_and_close
   440  
   441  set answers [list {0 {}} {1 SQLITE_NOMEM} \
   442                    {1 {callback requested query abort}} \
   443                    {1 {abort due to ROLLBACK}}]
   444  do_faultsim_test 9.1 -faults oom-transient -prep {
   445    catch { unset ::c }
   446    faultsim_restore_and_reopen
   447    sqlite3session S db main
   448    S attach *
   449  } -body {
   450    execsql {
   451      INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
   452    }
   453    set ::c [S changeset]
   454    set {} {}
   455  } -test {
   456    S delete
   457    eval faultsim_test_result $::answers
   458    if {[info exists ::c]} {
   459      set expected [normalize_list {
   460        {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}
   461      }]
   462      if { [changeset_to_list $::c] != $expected } {
   463        error "changeset mismatch"
   464      }
   465    }
   466  }
   467  
   468  faultsim_delete_and_reopen
   469  do_test 9.2.prep {
   470    execsql { 
   471      PRAGMA encoding = 'utf16';
   472      CREATE TABLE t1(a PRIMARY KEY, b);
   473      INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
   474    }
   475  } {}
   476  faultsim_save_and_close
   477  
   478  set answers [list {0 {}} {1 SQLITE_NOMEM} \
   479                    {1 {callback requested query abort}} \
   480                    {1 {abort due to ROLLBACK}}]
   481  do_faultsim_test 9.2 -faults oom-transient -prep {
   482    catch { unset ::c }
   483    faultsim_restore_and_reopen
   484    sqlite3session S db main
   485    S attach *
   486  } -body {
   487    execsql {
   488      UPDATE t1 SET b = 'xyz';
   489    }
   490    set ::c [S changeset]
   491    set {} {}
   492  } -test {
   493    S delete
   494    eval faultsim_test_result $::answers
   495    if {[info exists ::c]} {
   496      set expected [normalize_list {
   497        {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}}
   498      }]
   499      if { [changeset_to_list $::c] != $expected } {
   500        error "changeset mismatch"
   501      }
   502    }
   503  }
   504  
   505  #-------------------------------------------------------------------------
   506  # Test that if a conflict-handler encounters an OOM in 
   507  # sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE
   508  # anyway, the OOM is picked up by the sessions module.
   509  set bigstr [string repeat abcdefghij 100]
   510  faultsim_delete_and_reopen
   511  do_test 10.prep.1  {
   512    execsql {
   513      CREATE TABLE t1(a PRIMARY KEY, b);
   514      INSERT INTO t1 VALUES($bigstr, $bigstr);
   515    }
   516  
   517    sqlite3session S db main
   518    S attach *
   519    execsql { UPDATE t1 SET b = b||'x' }
   520    set C [S changeset]
   521    S delete
   522    execsql { UPDATE t1 SET b = b||'xyz' }
   523  } {}
   524  faultsim_save_and_close
   525  
   526  faultsim_restore_and_reopen
   527  do_test 10.prep.2  {
   528    proc xConflict {args} { return "ABORT" }
   529    list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
   530  } {1 SQLITE_ABORT}
   531  do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
   532  do_test 10.prep.4  {
   533    proc xConflict {args} { return "REPLACE" }
   534    list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
   535  } {0 {}}
   536  do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
   537  db close
   538  
   539  do_faultsim_test 10 -faults oom-tra* -prep {
   540    faultsim_restore_and_reopen
   541  } -body {
   542    sqlite3changeset_apply_replace_all db $::C 
   543  } -test {
   544    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   545    if {$testrc==0} {
   546      if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
   547        error "data does not look right"
   548      }
   549    }
   550  }
   551  
   552  #-------------------------------------------------------------------------
   553  # Test an OOM with an sqlite3changeset_apply() filter callback.
   554  #
   555  reset_db
   556  do_test 11.prep {
   557    execsql {
   558      CREATE TABLE t1(a PRIMARY KEY, b);
   559      CREATE TABLE t2(x PRIMARY KEY, y);
   560      BEGIN;
   561    }
   562  
   563    set ::cs [changeset_from_sql { 
   564      INSERT INTO t1 VALUES(1, 2);
   565      INSERT INTO t2 VALUES('x', 'y');
   566    }]
   567  
   568    execsql ROLLBACK
   569    set {} {}
   570  } {}
   571  
   572  proc filter {x} { return [string equal t1 $x] } 
   573  faultsim_save_and_close
   574  
   575  do_faultsim_test 11 -faults oom-tra* -prep {
   576    faultsim_restore_and_reopen
   577  } -body {
   578    sqlite3changeset_apply db $::cs {} filter
   579  } -test {
   580    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
   581    if {$testrc==0} {
   582      if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
   583        error "data does not look right"
   584      }
   585    }
   586  }
   587  
   588  
   589  finish_test