Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Modifications so that the sessions extension works with blob handles. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
82ac16c4f873d3bd7c22f36ba7b974b4 |
User & Date: | dan 2011-07-11 19:45:38.954 |
Context
2011-07-13
| ||
15:21 | Add the xFilter callback to the sqlite3changeset_apply() function. This callback allows the application to accept or reject changes on a per-table basis when applying a changeset. (check-in: 282474c42f user: dan tags: sessions) | |
2011-07-11
| ||
19:45 | Modifications so that the sessions extension works with blob handles. (check-in: 82ac16c4f8 user: dan tags: sessions) | |
2011-06-23
| ||
17:40 | Pull the latest version 3.7.7 release-candidate changes into the sessions branch. (check-in: 840bf9c2d9 user: drh tags: sessions) | |
Changes
Added ext/session/session6.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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | # 2011 July 11 # # 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 SQLite sessions extension. # Specifically, it tests that sessions work when the database is modified # using incremental blob handles. # 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 ifcapable !session {finish_test; return} set testprefix session6 proc do_then_apply_tcl {tcl {dbname main}} { proc xConflict args { return "OMIT" } set rc [catch { sqlite3session S db $dbname db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" { S attach $name } eval $tcl sqlite3changeset_apply db2 [S changeset] xConflict } msg] catch { S delete } if {$rc} {error $msg} } test_sqlite3_log x proc x {args} {puts $args} forcedelete test.db2 sqlite3 db2 test.db2 do_common_sql { CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE t2(c PRIMARY KEY, d); } # Test a blob update. # do_test 1.1 { do_then_apply_tcl { db eval { INSERT INTO t1 VALUES(1, 'helloworld') } db eval { INSERT INTO t2 VALUES(2, 'onetwothree') } } compare_db db db2 } {} do_test 1.2 { do_then_apply_tcl { set fd [db incrblob t1 b 1] puts -nonewline $fd 1234567890 close $fd } compare_db db db2 } {} # Test an attached database. # do_test 2.1 { forcedelete test.db3 file copy test.db2 test.db3 execsql { ATTACH 'test.db3' AS aux; } do_then_apply_tcl { set fd [db incrblob aux t2 d 1] puts -nonewline $fd fourfivesix close $fd } aux sqlite3 db3 test.db3 compare_db db2 db3 } {} db3 close db2 close finish_test |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; | | | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->keyinfo.nField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is designed to be called from within a pre-update callback ** only. It returns zero if the change that caused the callback was made |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 | assert( db->pPreUpdate==0 ); memset(&preupdate, 0, sizeof(PreUpdate)); if( op==SQLITE_UPDATE ){ iKey2 = v->aMem[iReg].u.i; }else{ iKey2 = iKey1; } preupdate.v = v; preupdate.pCsr = pCsr; preupdate.op = op; preupdate.iNewReg = iReg; preupdate.keyinfo.db = db; preupdate.keyinfo.enc = ENC(db); | > > > > | | 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | assert( db->pPreUpdate==0 ); memset(&preupdate, 0, sizeof(PreUpdate)); if( op==SQLITE_UPDATE ){ iKey2 = v->aMem[iReg].u.i; }else{ iKey2 = iKey1; } assert( pCsr->nField==pTab->nCol || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) ); preupdate.v = v; preupdate.pCsr = pCsr; preupdate.op = op; preupdate.iNewReg = iReg; preupdate.keyinfo.db = db; preupdate.keyinfo.enc = ENC(db); preupdate.keyinfo.nField = pTab->nCol; preupdate.iKey1 = iKey1; preupdate.iKey2 = iKey2; preupdate.iPKey = pTab->iPKey; db->pPreUpdate = &preupdate; db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); db->pPreUpdate = 0; |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ int iCol; /* Table column this handle is open on */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ }; /* ** This function is used by both blob_open() and blob_reopen(). It seeks ** the b-tree cursor associated with blob handle p to point to row iRow. ** If successful, SQLITE_OK is returned and subsequent calls to | > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ int iCol; /* Table column this handle is open on */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ char *zDb; /* Database name */ Table *pTab; /* Table object */ }; /* ** This function is used by both blob_open() and blob_reopen(). It seeks ** the b-tree cursor associated with blob handle p to point to row iRow. ** If successful, SQLITE_OK is returned and subsequent calls to |
︙ | ︙ | |||
190 191 192 193 194 195 196 197 198 199 200 201 202 203 | zErr = pParse->zErrMsg; pParse->zErrMsg = 0; } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* Now search pTab for the exact column. */ for(iCol=0; iCol<pTab->nCol; iCol++) { if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ break; } } | > > | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | zErr = pParse->zErrMsg; pParse->zErrMsg = 0; } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } pBlob->pTab = pTab; pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zName; /* Now search pTab for the exact column. */ for(iCol=0; iCol<pTab->nCol; iCol++) { if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ break; } } |
︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 | rc = SQLITE_ABORT; }else{ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is ** returned, clean-up the statement handle. */ assert( db == v->db ); sqlite3BtreeEnterCursor(p->pCsr); rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); sqlite3BtreeLeaveCursor(p->pCsr); if( rc==SQLITE_ABORT ){ sqlite3VdbeFinalize(v); p->pStmt = 0; }else{ db->errCode = rc; | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | rc = SQLITE_ABORT; }else{ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is ** returned, clean-up the statement handle. */ assert( db == v->db ); sqlite3BtreeEnterCursor(p->pCsr); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( xCall==sqlite3BtreePutData ){ /* If a pre-update hook is registered and this is a write cursor, ** invoke it here. ** ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this ** operation should really be an SQLITE_UPDATE. This is probably ** incorrect, but is convenient because at this point the new.* values ** are not easily obtainable. And for the sessions module, an ** SQLITE_UPDATE where the PK columns do not change is handled in the ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually ** slightly more efficient). Since you cannot write to a PK column ** using the incremental-blob API, this works. For the sessions module ** anyhow. */ sqlite3_int64 iKey; sqlite3BtreeKeySize(p->pCsr, &iKey); sqlite3VdbePreUpdateHook( v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 ); } #endif rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); sqlite3BtreeLeaveCursor(p->pCsr); if( rc==SQLITE_ABORT ){ sqlite3VdbeFinalize(v); p->pStmt = 0; }else{ db->errCode = rc; |
︙ | ︙ |