]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improve coverage of session module code.
authordan <dan@noemail.net>
Fri, 25 Mar 2011 10:52:01 +0000 (10:52 +0000)
committerdan <dan@noemail.net>
Fri, 25 Mar 2011 10:52:01 +0000 (10:52 +0000)
FossilOrigin-Name: 666123c8d07be87d477e67b1cebef2b0fba5b4bc

ext/session/session2.test
ext/session/session3.test
ext/session/session4.test [new file with mode: 0644]
ext/session/sqlite3session.c
ext/session/sqlite3session.h
ext/session/test_session.c
manifest
manifest.uuid

index a4776d80e5b87730aa914f1bf858d07684c28d9f..6662173d49c1c80b34c145bf3177fca5912b93b0 100644 (file)
@@ -377,4 +377,149 @@ do_iterator_test 6.1.7 * {
   {UPDATE t2 0 X. {i 4 t y} {{} {} t new}}
 }
 
+sqlite3session S db main
+do_execsql_test 6.2.1 {
+  SELECT indirect(0);
+  SELECT indirect(-1);
+  SELECT indirect(45);
+  SELECT indirect(-100);
+} {0 0 1 1}
+S delete
+
+#-------------------------------------------------------------------------
+# Test that if a conflict-handler that has been passed either NOTFOUND or
+# CONSTRAINT returns REPLACE - the sqlite3changeset_apply() call returns
+# MISUSE and rolls back any changes made so far.
+#
+#   7.1.*: NOTFOUND conflict-callback.
+#   7.2.*: CONSTRAINT conflict-callback.
+#
+proc xConflict {args} {return REPLACE}
+test_reset
+
+do_execsql_test 7.1.1 {
+  CREATE TABLE t1(a PRIMARY KEY, b);
+  INSERT INTO t1 VALUES(1, 'one');
+  INSERT INTO t1 VALUES(2, 'two');
+}
+do_test 7.1.2 {
+  execsql {
+    CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
+    INSERT INTO t1 VALUES(1, 'one');
+  } db2
+} {}
+do_test 7.1.3 {
+  set changeset [changeset_from_sql {
+    UPDATE t1 SET b = 'five' WHERE a = 1;
+    UPDATE t1 SET b = 'six' WHERE a = 2;
+  }]
+  set x [list]
+  sqlite3session_foreach c $changeset { lappend x $c }
+  set x
+} [list \
+  {UPDATE t1 0 X. {i 1 t one} {{} {} t five}} \
+  {UPDATE t1 0 X. {i 2 t two} {{} {} t six}}  \
+]
+do_test 7.1.4 {
+  list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
+} {1 SQLITE_MISUSE}
+do_test 7.1.5 { execsql { SELECT * FROM t1 } db2 } {1 one}
+
+do_test 7.2.1 {
+  set changeset [changeset_from_sql { UPDATE t1 SET b = NULL WHERE a = 1 }]
+
+  set x [list]
+  sqlite3session_foreach c $changeset { lappend x $c }
+  set x
+} [list \
+  {UPDATE t1 0 X. {i 1 t five} {{} {} n {}}} \
+]
+do_test 7.2.2 {
+  list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
+} {1 SQLITE_MISUSE}
+do_test 7.2.3 { execsql { SELECT * FROM t1 } db2 } {1 one}
+
+#-------------------------------------------------------------------------
+# Test that if a conflict-handler returns ABORT, application of the 
+# changeset is rolled back and the sqlite3changeset_apply() method returns
+# SQLITE_ABORT.
+#
+# Also test that the same thing happens if a conflict handler returns an
+# unrecognized integer value. Except, in this case SQLITE_MISUSE is returned
+# instead of SQLITE_ABORT.
+#
+foreach {tn conflict_return apply_return} {
+  1    ABORT   SQLITE_ABORT
+  2    567     SQLITE_MISUSE
+} {
+  test_reset
+  proc xConflict {args} [list return $conflict_return]
+
+  do_test 8.$tn.0 {
+    do_common_sql { 
+      CREATE TABLE t1(x, y, PRIMARY KEY(x, y));
+      INSERT INTO t1 VALUES('x', 'y');
+    }
+    execsql { INSERT INTO t1 VALUES('w', 'w') }
+
+    set changeset [changeset_from_sql { DELETE FROM t1 WHERE 1 }]
+
+    set x [list]
+    sqlite3session_foreach c $changeset { lappend x $c }
+    set x
+  } [list \
+    {DELETE t1 0 XX {t w t w} {}} \
+    {DELETE t1 0 XX {t x t y} {}} \
+  ]
+
+  do_test 8.$tn.1 {
+    list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
+  } [list 1 $apply_return]
+
+  do_test 8.$tn.2 {
+    execsql {SELECT * FROM t1} db2
+  } {x y}
+}
+
+
+#-------------------------------------------------------------------------
+# Try to cause an infinite loop as follows:
+#
+#   1. Have a changeset insert a row that causes a CONFLICT callback,
+#   2. Have the conflict handler return REPLACE,
+#   3. After the session module deletes the conflicting row, have a trigger
+#      re-insert it.
+#   4. Goto step 1...
+#
+# This doesn't work, as the second invocation of the conflict handler is a
+# CONSTRAINT, not a CONFLICT. There is at most one CONFLICT callback for
+# each change in the changeset.
+#
+test_reset
+proc xConflict {type args} { 
+  if {$type == "CONFLICT"} { return REPLACE }
+  return OMIT
+}
+do_test 9.1 {
+  execsql {
+    CREATE TABLE t1(a PRIMARY KEY, b);
+  }
+  execsql {
+    CREATE TABLE t1(a PRIMARY KEY, b);
+    INSERT INTO t1 VALUES('x', 2);
+    CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
+      INSERT INTO t1 VALUES(old.a, old.b);
+    END;
+  } db2
+} {}
+do_test 9.2 {
+  set changeset [changeset_from_sql { INSERT INTO t1 VALUES('x', 1) }]
+  sqlite3changeset_apply db2 $changeset xConflict
+} {}
+do_test 9.3 {
+  execsql { SELECT * FROM t1 } db2
+} {x 2}
+
+
+
 finish_test
index d0d2c31fa401dadedf4637db2f17ff43c0db1f80..a0946f30990a1ac3f2f63363e00b3e179a7e48da 100644 (file)
@@ -8,8 +8,10 @@
 #    May you share freely, never taking more than you give.
 #
 #***********************************************************************
-# This file implements regression tests for the session module.
-#
+# This file implements regression tests for the session module. More
+# specifically, it focuses on testing the session modules response to
+# database schema modifications and mismatches.
+# 
 
 if {![info exists testdir]} {
   set testdir [file join [file dirname [info script]] .. .. test]
diff --git a/ext/session/session4.test b/ext/session/session4.test
new file mode 100644 (file)
index 0000000..c69fa85
--- /dev/null
@@ -0,0 +1,62 @@
+# 2011 March 25
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for the session module. 
+# 
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+
+set testprefix session4
+
+do_test 1.0 {
+  execsql {
+    CREATE TABLE x(a, b, c, d, e, PRIMARY KEY(c, e));
+    INSERT INTO x VALUES(65.21, X'28B0', 16.35, NULL, 'doers');
+    INSERT INTO x VALUES(NULL, 78.49, 2, X'60', -66);
+    INSERT INTO x VALUES('cathedral', NULL, 35, NULL, X'B220937E80A2D8');
+    INSERT INTO x VALUES(NULL, 'masking', -91.37, NULL, X'596D');
+    INSERT INTO x VALUES(19, 'domains', 'espouse', -94, 'throw');
+  }
+
+  sqlite3session S db main
+  set changeset [changeset_from_sql {
+    DELETE FROM x WHERE e = -66;
+    UPDATE x SET a = 'parameterizable', b = 31.8 WHERE c = 35;
+    INSERT INTO x VALUES(-75.61, -17, 16.85, NULL, X'D73DB02678');
+  }]
+  set {} {}
+} {}
+
+
+# This currently causes crashes. sqlite3changeset_invert() does not handle
+# corrupt changesets well.
+if 0 {
+  do_test 1.1 {
+    for {set i 0} {$i < [string length $changeset]} {incr i} {
+      set before [string range $changeset 0 [expr $i-1]]
+      set after  [string range $changeset [expr $i+1] end]
+      for {set j 10} {$j < 260} {incr j} {
+        set x [binary format "a*ca*" $before $j $after]
+        catch { sqlite3changeset_invert $x }
+      }
+    }
+  } {}
+}
+
+do_test 1.2 {
+  set x [binary format "ca*" 0 [string range $changeset 1 end]]
+  list [catch { sqlite3changeset_invert $x } msg] $msg
+} {1 SQLITE_CORRUPT}
+
+finish_test
index ffc2e13f5e5aa19b6a97126561555c78a9696b54..36d83e6b8e216e17c026254f0226e902ba9903f8 100644 (file)
@@ -2255,7 +2255,8 @@ static int sessionConflictHandler(
   if( rc==SQLITE_OK ){
     switch( res ){
       case SQLITE_CHANGESET_REPLACE:
-        if( pbReplace ) *pbReplace = 1;
+        assert( pbReplace );
+        *pbReplace = 1;
         break;
 
       case SQLITE_CHANGESET_OMIT:
index 5d19379ec8b0f9a195f2b55909c2bd9b4c5de053..bcb901667c79597aaaeaa348129378bcc02417e7 100644 (file)
@@ -513,6 +513,9 @@ int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
 ** It is the responsibility of the caller to eventually call sqlite3_free()
 ** on the *ppOut pointer to free the buffer allocation following a successful 
 ** call to this function.
+**
+** WARNING/TODO: This function currently assumes that the input is a valid
+** changeset. If it is not, the results are undefined.
 */
 int sqlite3changeset_invert(
   int nIn, void *pIn,             /* Input changeset */
index e3d6d60c22c56f45f34cb98aafa88fa4612b4005..4befd0fe6850bd60fdd606d9fec9c968ca108966 100644 (file)
@@ -17,7 +17,7 @@ static int test_session_error(Tcl_Interp *interp, int rc){
 **          $session changeset
 **          $session delete
 **          $session enable BOOL
-**          $session indirect BOOL
+**          $session indirect INTEGER
 */
 static int test_session_cmd(
   void *clientData,
@@ -93,7 +93,7 @@ static int test_session_cmd(
 
     case 4: {      /* indirect */
       int val;
-      if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR;
+      if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
       val = sqlite3session_indirect(pSession, val);
       Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
       break;
@@ -268,18 +268,59 @@ static int test_conflict_handler(
   }
 
   /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append
-  ** the conflicting row. */
+  ** the conflicting row.  */
   if( eConf==SQLITE_CHANGESET_DATA || eConf==SQLITE_CHANGESET_CONFLICT ){
     int i;
     Tcl_Obj *pConflict = Tcl_NewObj();
     for(i=0; i<nCol; i++){
+      int rc;
       sqlite3_value *pVal;
-      sqlite3changeset_conflict(pIter, i, &pVal);
+      rc = sqlite3changeset_conflict(pIter, i, &pVal);
+      assert( rc==SQLITE_OK );
       test_append_value(pConflict, pVal);
     }
     Tcl_ListObjAppendElement(0, pEval, pConflict);
   }
 
+  /***********************************************************************
+  ** This block is purely for testing some error conditions.
+  */
+  if( eConf==SQLITE_CHANGESET_CONSTRAINT || eConf==SQLITE_CHANGESET_NOTFOUND ){
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_conflict(pIter, 0, &pVal);
+    assert( rc==SQLITE_MISUSE );
+  }else{
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_conflict(pIter, -1, &pVal);
+    assert( rc==SQLITE_RANGE );
+    rc = sqlite3changeset_conflict(pIter, nCol, &pVal);
+    assert( rc==SQLITE_RANGE );
+  }
+  if( op==SQLITE_DELETE ){
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_new(pIter, 0, &pVal);
+    assert( rc==SQLITE_MISUSE );
+  }else{
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_new(pIter, -1, &pVal);
+    assert( rc==SQLITE_RANGE );
+    rc = sqlite3changeset_new(pIter, nCol, &pVal);
+    assert( rc==SQLITE_RANGE );
+  }
+  if( op==SQLITE_INSERT ){
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_old(pIter, 0, &pVal);
+    assert( rc==SQLITE_MISUSE );
+  }else{
+    sqlite3_value *pVal;
+    int rc = sqlite3changeset_old(pIter, -1, &pVal);
+    assert( rc==SQLITE_RANGE );
+    rc = sqlite3changeset_old(pIter, nCol, &pVal);
+    assert( rc==SQLITE_RANGE );
+  }
+  /* End of testing block
+  ***********************************************************************/
+
   if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL) ){
     Tcl_BackgroundError(interp);
   }else{
@@ -291,13 +332,7 @@ static int test_conflict_handler(
     }else if( test_obj_eq_string(pRes, "ABORT") ){
       ret = SQLITE_CHANGESET_ABORT;
     }else{
-      Tcl_IncrRefCount(pRes);
-      Tcl_ResetResult(interp);
-      Tcl_AppendResult(interp, "unrecognized conflict handler return: \"", 
-          Tcl_GetString(pRes), "\"", 0
-      );
-      Tcl_DecrRefCount(pRes);
-      Tcl_BackgroundError(interp);
+      Tcl_GetIntFromObj(0, pRes, &ret);
     }
   }
 
index 1f7e7a7bdd4de6488e77b00b6f940e6d6fed76be..a8b17c0fc5b50650922a79bfd3f2cb1a1bf1bd82 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\shandling\sof\sschema\schanges\smid-session.
-D 2011-03-24T16:53:57
+C Improve\scoverage\sof\ssession\smodule\scode.
+D 2011-03-25T10:52:02
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -100,13 +100,14 @@ F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
 F ext/session/session1.test b2da15b9d727d7f4e5fe95599b32b92d93b5a970
-F ext/session/session2.test 8da094318ac88953478c43d0bfb0aa723ee0e379
-F ext/session/session3.test c58ebb3273a23da9b5f4eb202d522aa759530a4c
+F ext/session/session2.test f2f0bad3f27c9084f5f51444d017ede485cee009
+F ext/session/session3.test 5b2e9d0b94af5d7770f89e020d83b838584ccc64
+F ext/session/session4.test 3b39b468bf871f3b7825f64f9ce9f3849ff4b156
 F ext/session/session_common.tcl fb91560b6dbd086010df8b3a137a452f1ac21a28
 F ext/session/sessionfault.test d7e6154a30e85622d0733b1a1e3c63e9b8b7004b
-F ext/session/sqlite3session.c 33a5d4be9c22099aed8e7f6c80b63540953e84c2
-F ext/session/sqlite3session.h 900d900bb6a827f84754fc252a05638e0f413a6e
-F ext/session/test_session.c e0f500ec5e20478afc2c7998133e8acea7ec5104
+F ext/session/sqlite3session.c 8057ed55d25c4d487ec8b68a3ee1c2c25dd543da
+F ext/session/sqlite3session.h f284bac51c12de0e0096fc986e61f5ae6b9e5be5
+F ext/session/test_session.c 5f186a9f45958620ebc0609099538eb7cabdfe84
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F main.mk ae0868e05c76eaa8a0ae3d6927a949b1c8e810d7
@@ -924,7 +925,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 506a0d7a710e1ff2f367821e73f5080fcf63fbc5
-R 3234ab73592ecae31c60c0a6f501bbfd
+P 76d2d2ad3b2a5171393b7894f35f463ff284e53b
+R b7843eac9e1de41d0b79cfdd795816c8
 U dan
-Z 29af2cd6e2d7044fdb214ebc342ad1ba
+Z 4c90a35f73d811b840852d363dcb9fdc
index 5f9877e0ed2ca0fec5cc1383c1856b17f435b67d..c8072ced973513f01428886a57e511d8aaad7ccc 100644 (file)
@@ -1 +1 @@
-76d2d2ad3b2a5171393b7894f35f463ff284e53b
\ No newline at end of file
+666123c8d07be87d477e67b1cebef2b0fba5b4bc
\ No newline at end of file