1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Improve coverage of session module code.

FossilOrigin-Name: 666123c8d07be87d477e67b1cebef2b0fba5b4bc
This commit is contained in:
dan
2011-03-25 10:52:01 +00:00
parent a9605b9125
commit f51e5f6c99
8 changed files with 274 additions and 25 deletions

View 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

View 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]

62
ext/session/session4.test Normal file
View File

@ -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

View 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:

View 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 */

View 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);
}
}