Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improve coverage of session module code. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
666123c8d07be87d477e67b1cebef2b0 |
User & Date: | dan 2011-03-25 10:52:02.000 |
Context
2011-03-25
| ||
19:06 | Improve coverage of session module a bit more. (check-in: 4255a9f609 user: dan tags: sessions) | |
10:52 | Improve coverage of session module code. (check-in: 666123c8d0 user: dan tags: sessions) | |
2011-03-24
| ||
16:53 | Fix handling of schema changes mid-session. (check-in: 76d2d2ad3b user: dan tags: sessions) | |
Changes
Changes to ext/session/session2.test.
︙ | ︙ | |||
372 373 374 375 376 377 378 379 380 | SELECT indirect(1); DELETE FROM t2 WHERE x = 4; SELECT indirect(0); INSERT INTO t2 VALUES(4, 'new'); } { {UPDATE t2 0 X. {i 4 t y} {{} {} t new}} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | SELECT indirect(1); DELETE FROM t2 WHERE x = 4; SELECT indirect(0); INSERT INTO t2 VALUES(4, 'new'); } { {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 |
Changes to ext/session/session3.test.
1 2 3 4 5 6 7 8 9 10 | # 2011 March 24 # # 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. # #*********************************************************************** | | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2011 March 24 # # 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. 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] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl |
︙ | ︙ |
Added ext/session/session4.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 |
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
2251 2252 2253 2254 2255 2256 2257 | res = xConflict(pCtx, eType+1, pIter); if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE; } if( rc==SQLITE_OK ){ switch( res ){ case SQLITE_CHANGESET_REPLACE: | > | | 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 | res = xConflict(pCtx, eType+1, pIter); if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE; } if( rc==SQLITE_OK ){ switch( res ){ case SQLITE_CHANGESET_REPLACE: assert( pbReplace ); *pbReplace = 1; break; case SQLITE_CHANGESET_OMIT: break; case SQLITE_CHANGESET_ABORT: rc = SQLITE_ABORT; |
︙ | ︙ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
509 510 511 512 513 514 515 516 517 518 519 520 521 522 | ** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and ** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are ** zeroed and an SQLite error code returned. ** ** 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. */ int sqlite3changeset_invert( int nIn, void *pIn, /* Input changeset */ int *pnOut, void **ppOut /* OUT: Inverse of input */ ); /* | > > > | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | ** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and ** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are ** zeroed and an SQLite error code returned. ** ** 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 */ int *pnOut, void **ppOut /* OUT: Inverse of input */ ); /* |
︙ | ︙ |
Changes to ext/session/test_session.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | } /* ** Tclcmd: $session attach TABLE ** $session changeset ** $session delete ** $session enable BOOL | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | } /* ** Tclcmd: $session attach TABLE ** $session changeset ** $session delete ** $session enable BOOL ** $session indirect INTEGER */ static int test_session_cmd( void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ |
︙ | ︙ | |||
89 90 91 92 93 94 95 | val = sqlite3session_enable(pSession, val); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val)); break; } case 4: { /* indirect */ int val; | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | val = sqlite3session_enable(pSession, val); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val)); break; } case 4: { /* indirect */ int val; if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR; val = sqlite3session_indirect(pSession, val); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val)); break; } } return TCL_OK; |
︙ | ︙ | |||
264 265 266 267 268 269 270 | sqlite3changeset_new(pIter, i, &pVal); test_append_value(pNew, pVal); } Tcl_ListObjAppendElement(0, pEval, pNew); } /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | < < < | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | sqlite3changeset_new(pIter, i, &pVal); test_append_value(pNew, pVal); } Tcl_ListObjAppendElement(0, pEval, pNew); } /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append ** 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; 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{ Tcl_Obj *pRes = Tcl_GetObjResult(interp); if( test_obj_eq_string(pRes, "OMIT") || test_obj_eq_string(pRes, "") ){ ret = SQLITE_CHANGESET_OMIT; }else if( test_obj_eq_string(pRes, "REPLACE") ){ ret = SQLITE_CHANGESET_REPLACE; }else if( test_obj_eq_string(pRes, "ABORT") ){ ret = SQLITE_CHANGESET_ABORT; }else{ Tcl_GetIntFromObj(0, pRes, &ret); } } Tcl_DecrRefCount(pEval); return ret; } |
︙ | ︙ |