diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index c4053e3dbe..573b2ff3fc 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -15,8 +15,32 @@ typedef struct sqlite3_session sqlite3_session; typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; /* -** Create a session object. This session object will record changes to -** database zDb attached to connection db. +** Create a new session object attached to database handle db. If successful, +** a pointer to the new object is written to *ppSession and SQLITE_OK is +** returned. If an error occurs, *ppSession is set to NULL and an SQLite +** error code (e.g. [SQLITE_NOMEM]) is returned. +** +** It is possible to create multiple session objects attached to a single +** database handle. +** +** Session objects created using this function should be deleted using the +** [sqlite3session_delete()] function before the database handle that they +** are attached to is itself closed. If the database handle is closed before +** the session object is deleted, then the results of calling any session +** module function, including [sqlite3session_delete()] on the session object +** are undefined. +** +** Because the session module uses the [sqlite3_preupdate_hook()] API, it +** is not possible for an application to register a pre-update hook on a +** database handle that has one or more session objects attached. Nor is +** it possible to create a session object attached to a database handle for +** which a pre-update hook is already defined. The results of attempting +** either of these things are undefined. +** +** The session object will be used to create changesets for tables in +** database zDb, where zDb is either "main", or "temp", or the name of an +** attached database. It is not an error if database zDb does not exist +** to the database when the session object is created. */ int sqlite3session_create( sqlite3 *db, /* Database handle */ @@ -24,6 +48,18 @@ int sqlite3session_create( sqlite3_session **ppSession /* OUT: New session object */ ); +/* +** Delete a session object previously allocated using +** [sqlite3session_create()]. Once a session object has been deleted, the +** results of attempting to use pSession with any other session module +** function are undefined. +** +** Session objects must be deleted before the database handle to which they +** are attached is closed. Refer to the documentation for +** [sqlite3session_create()] for details. +*/ +void sqlite3session_delete(sqlite3_session *pSession); + /* ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When @@ -52,11 +88,16 @@ int sqlite3session_attach( ); /* -** Obtain a changeset object containing all changes recorded by the -** session object passed as the first argument. +** Obtain a changeset containing changes to the tables attached to the +** session object passed as the first argument. If successful, +** set *ppChangeset to point to a buffer containing the changeset +** and *pnChangeset to the size of the changeset in bytes before returning +** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to +** zero and return an SQLite error code. ** -** It is the responsibility of the caller to eventually free the buffer -** using sqlite3_free(). +** Following a successful call to this function, it is the responsibility of +** the caller to eventually free the buffer that *ppChangeset points to using +** [sqlite3_free()]. */ int sqlite3session_changeset( sqlite3_session *pSession, /* Session object */ @@ -64,11 +105,6 @@ int sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); -/* -** Delete a session object previously allocated using sqlite3session_create(). -*/ -void sqlite3session_delete(sqlite3_session *pSession); - /* ** Create an iterator used to iterate through the contents of a changeset. */ diff --git a/manifest b/manifest index 4061dea410..796df369be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\ssqlite3_transaction_hook()\sAPI. -D 2011-03-16T09:49:15 +C Add\sthe\ssqlite3_preupdate_new()\sAPI,\sfor\sretrieving\sthe\snew.*\svalues\sfrom\swithin\sa\spre-update\scallback. +D 2011-03-16T19:59:19 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -99,7 +99,7 @@ F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/sqlite3session.c 9b8d123418c024f6851163375fca99042757772f -F ext/session/sqlite3session.h 01aac9a1185b7db6716217f3aa3f7a835ab864b9 +F ext/session/sqlite3session.h 63045871564085669b5cb1fb92e6efc2e1b1120a F ext/session/test_session.c 2559ef68e421c7fb83e2c19ef08a17343b70d535 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 @@ -180,13 +180,13 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c d24406c45dd2442eb2eeaac413439066b149c944 F src/shell.c 649c51979812f77f97507024a4cea480c6862b8b -F src/sqlite.h.in 8da2897e3c9b251b29aa48d11bfb1f30f1de0733 +F src/sqlite.h.in 992c54d9bd451a041fb0b74fb5cd3b14db98e544 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqliteInt.h db209477de559911d7f14c1d208cbf2bc46e9224 F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 0aa7e768b3bd72bf4c4b0312c9a84d6cdedb7638 +F src/tclsqlite.c fc0321c62a3c3929b9b0659b94b7d37bac84e769 F src/test1.c 9020310c7617234b33fd1c3064f89524db25f290 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc @@ -228,15 +228,15 @@ F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080 F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852 -F src/update.c 1b9a82ede7df15e76ed86c6a3cbe4ce0f21eaa9b +F src/update.c 18a862e3e08377a5afb26d6f16182a8f700a1ca7 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/util.c ab1c92426494f499f42b9e307537b03e923d75c1 F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f -F src/vdbe.c 117644088f89c40b01c917d06f7b2ed706ca125f +F src/vdbe.c fbf11bd681fd2313aaf59fb2fd7f02cd8324a88a F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2 -F src/vdbeInt.h 9dd04435bd5a68e30cd07e7a91c17b062bf1c23d -F src/vdbeapi.c 256029b0a2ed2373ebbcce7a497b3a702a52689b -F src/vdbeaux.c f789da7d55231d779c26deaf5a1e6ccb3e550c09 +F src/vdbeInt.h 20d13da932eed0667a2e2383a9cb0f80099a5fd3 +F src/vdbeapi.c 3066456f64fb10c9c5151c684b5f5e8d67a5f4f2 +F src/vdbeaux.c 896844f9bf663202b3afa8c139e2caddcf855765 F src/vdbeblob.c 18955f0ee6b133cd08e1592010cb9a6b11e9984c F src/vdbemem.c 0fa2ed786cd207d5b988afef3562a8e663a75b50 F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5 @@ -474,7 +474,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c -F test/hook.test 85059721ef537317af679aca5435f94ab316d074 +F test/hook.test d0a277022888caf75ae1d4ec79917668f2f0f2e6 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 @@ -913,7 +913,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P f2930840e4af3d7d9cb199d316502932fcbbb867 -R fadac1472cdeb40a0e5c985ec43cbe17 +P b0015a1cfe63c924ee5f250aa08460522882009b +R 4b62a15aa3feb03b0e54e624839e53a1 U dan -Z 22d8aae45d188380f4801c8d2b2db4e1 +Z 0b4af75dcf5347bcbba47251e978ee95 diff --git a/manifest.uuid b/manifest.uuid index 2664939330..69c5255486 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b0015a1cfe63c924ee5f250aa08460522882009b \ No newline at end of file +526545c49f64d9063d1b888cfc14ece62fa3c13c \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d3c9ac5f24..0f920bd6dd 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -6366,8 +6366,8 @@ SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook( void* ); SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); -SQLITE_EXPERIMENTAL int sqlite3_preupdate_modified(sqlite3 *, int, int *); SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *); +SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); /* ** Undo the hack that converts floating point types to integer for diff --git a/src/tclsqlite.c b/src/tclsqlite.c index b5ac5faf88..b3f98126c0 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2846,9 +2846,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } case DB_PREUPDATE: { - static const char *azSub[] = {"count", "hook", "modified", "old", 0}; + static const char *azSub[] = {"count", "hook", "new", "old", 0}; enum DbPreupdateSubCmd { - PRE_COUNT, PRE_HOOK, PRE_MODIFIED, PRE_OLD + PRE_COUNT, PRE_HOOK, PRE_NEW, PRE_OLD }; int iSub; @@ -2875,9 +2875,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } - case PRE_MODIFIED: + case PRE_NEW: case PRE_OLD: { int iIdx; + sqlite3_value *pValue; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 3, objv, "INDEX"); return TCL_ERROR; @@ -2886,21 +2887,17 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ return TCL_ERROR; } - if( iSub==PRE_MODIFIED ){ - int iRes; - rc = sqlite3_preupdate_modified(pDb->db, iIdx, &iRes); - if( rc==SQLITE_OK ) Tcl_SetObjResult(interp, Tcl_NewIntObj(iRes)); - }else{ - sqlite3_value *pValue; - assert( iSub==PRE_OLD ); + if( iSub==PRE_OLD ){ rc = sqlite3_preupdate_old(pDb->db, iIdx, &pValue); - if( rc==SQLITE_OK ){ - Tcl_Obj *pObj = Tcl_NewStringObj(sqlite3_value_text(pValue), -1); - Tcl_SetObjResult(interp, pObj); - } + }else{ + assert( iSub==PRE_NEW ); + rc = sqlite3_preupdate_new(pDb->db, iIdx, &pValue); } - if( rc!=SQLITE_OK ){ + if( rc==SQLITE_OK ){ + Tcl_Obj *pObj = Tcl_NewStringObj(sqlite3_value_text(pValue), -1); + Tcl_SetObjResult(interp, pObj); + }else{ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); return TCL_ERROR; } diff --git a/src/update.c b/src/update.c index ea8f95ad56..4bf4a880b1 100644 --- a/src/update.c +++ b/src/update.c @@ -493,7 +493,13 @@ void sqlite3Update( /* If changing the rowid value, or if there are foreign key constraints ** to process, delete the old record. Otherwise, add a noop OP_Delete ** to invoke the pre-update hook. + ** + ** That (regNew==regnewRowid+1) is true is also important for the + ** pre-update hook. If hte caller invokes preupdate_new(), the returned + ** value is copied from memory cell (regNewRowid+1+iCol), where iCol + ** is the column index supplied by the user. */ + assert( regNew==regNewRowid+1 ); sqlite3VdbeAddOp3(v, OP_Delete, iCur, OPFLAG_ISUPDATE | ((hasFK || chngRowid) ? 0 : OPFLAG_ISNOOP), regNewRowid diff --git a/src/vdbe.c b/src/vdbe.c index 041e93025b..1ef59d0a39 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3881,9 +3881,7 @@ case OP_InsertInt: { && pOp->p4.z && (!(pOp->p5 & OPFLAG_ISUPDATE) || pC->rowidIsValid==0) ){ - sqlite3VdbePreUpdateHook(p, pC, - pC->rowidIsValid ? op : SQLITE_INSERT, zDb, zTbl, iKey, iKey - ); + sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, zTbl, iKey, pOp->p2); } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; @@ -3985,7 +3983,7 @@ case OP_Delete: { sqlite3VdbePreUpdateHook(p, pC, (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, zDb, zTbl, iKey, - (opflags & OPFLAG_ISUPDATE) ? aMem[pOp->p3].u.i : iKey + pOp->p3 ); } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 0b4178aacd..447e025e94 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -336,11 +336,15 @@ struct Vdbe { ** sqlite3_preupdate_*() API functions. */ struct PreUpdate { + Vdbe *v; VdbeCursor *pCsr; /* Cursor to read old values from */ int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ u8 *aRecord; /* old.* database record */ KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ + UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ + int iNewReg; /* Register for new.* values */ + Mem *aNew; /* Array of new.* values */ }; /* @@ -400,7 +404,7 @@ void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); void sqlite3VdbePreUpdateHook( - Vdbe *, VdbeCursor *, int, const char*, const char*, i64, i64); + Vdbe *, VdbeCursor *, int, const char*, const char*, i64, int); #ifdef SQLITE_DEBUG void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 0d5fd94021..66f8c0857f 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1329,10 +1329,16 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ return v; } +/* +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or deleted. +*/ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; + /* Test that this call is being made from within an SQLITE_DELETE or + ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ if( !p || p->op==SQLITE_INSERT ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_old_out; @@ -1342,6 +1348,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ goto preupdate_old_out; } + /* If the old.* record has not yet been loaded into memory, do so now. */ if( p->pUnpacked==0 ){ u32 nRecord; u8 *aRecord; @@ -1372,26 +1379,78 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ return sqlite3ApiExit(db, rc); } +/* +** 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->pCsr->nField : 0); } -int sqlite3_preupdate_modified(sqlite3 *db, int iIdx, int *pbMod){ +/* +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or inserted. +*/ +int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; int rc = SQLITE_OK; + Mem *pMem; - if( !p || p->op!=SQLITE_UPDATE ){ + if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; - goto preupdate_mod_out; + goto preupdate_new_out; } if( iIdx>=p->pCsr->nField || iIdx<0 ){ rc = SQLITE_RANGE; - goto preupdate_mod_out; + goto preupdate_new_out; } - *pbMod = 1; - preupdate_mod_out: + if( p->op==SQLITE_INSERT ){ + /* For an INSERT, memory cell p->iNewReg contains the serialized record + ** that is being inserted. Deserialize it. */ + UnpackedRecord *pUnpack = p->pNewUnpacked; + if( !pUnpack ){ + Mem *pData = &p->v->aMem[p->iNewReg]; + rc = sqlite3VdbeMemExpandBlob(pData); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + pUnpack = sqlite3VdbeRecordUnpack(&p->keyinfo, pData->n, pData->z, 0, 0); + if( !pUnpack ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + p->pNewUnpacked = pUnpack; + } + if( iIdx>=pUnpack->nField ){ + pMem = (sqlite3_value *)columnNullValue(); + }else{ + pMem = &pUnpack->aMem[iIdx]; + sqlite3VdbeMemStoreType(pMem); + } + }else{ + /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required + ** value. Make a copy of the cell contents and return a pointer to it. + ** It is not safe to return a pointer to the memory cell itself as the + ** caller may modify the value text encoding. + */ + assert( p->op==SQLITE_UPDATE ); + if( !p->aNew ){ + p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); + if( !p->aNew ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + } + pMem = &p->aNew[iIdx]; + if( pMem->flags==0 ){ + rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + sqlite3VdbeMemStoreType(pMem); + } + } + *ppValue = pMem; + + preupdate_new_out: sqlite3Error(db, rc, 0); return sqlite3ApiExit(db, rc); } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 1c870c2404..9749251585 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3176,15 +3176,24 @@ void sqlite3VdbePreUpdateHook( const char *zDb, /* Database name */ const char *zTbl, /* Table name */ i64 iKey1, /* Initial key value */ - i64 iKey2 /* Final key value */ + int iReg /* Register for new.* record */ ){ sqlite3 *db = v->db; + i64 iKey2; PreUpdate preupdate; 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); preupdate.keyinfo.nField = pCsr->nField; @@ -3195,5 +3204,15 @@ void sqlite3VdbePreUpdateHook( if( preupdate.pUnpacked ){ sqlite3VdbeDeleteUnpackedRecord(preupdate.pUnpacked); } + if( preupdate.pNewUnpacked ){ + sqlite3VdbeDeleteUnpackedRecord(preupdate.pNewUnpacked); + } + if( preupdate.aNew ){ + int i; + for(i=0; inField; i++){ + sqlite3VdbeMemRelease(&preupdate.aNew[i]); + } + sqlite3_free(preupdate.aNew); + } } diff --git a/test/hook.test b/test/hook.test index 0ba4b19e40..001e8ddf4a 100644 --- a/test/hook.test +++ b/test/hook.test @@ -405,11 +405,17 @@ proc do_preupdate_test {tn sql x} { proc preupdate_hook {args} { set type [lindex $args 0] eval lappend ::preupdate $args - if {$type != "SQLITE_INSERT"} { + if {$type != "INSERT"} { for {set i 0} {$i < [db preupdate count]} {incr i} { lappend ::preupdate [db preupdate old $i] } } + if {$type != "DELETE"} { + for {set i 0} {$i < [db preupdate count]} {incr i} { + set rc [catch { db preupdate new $i } v] + lappend ::preupdate $v + } + } } db close @@ -433,28 +439,28 @@ do_execsql_test 7.0 { do_preupdate_test 7.1.1 { INSERT INTO t1 VALUES('x', 'y') -} {INSERT main t1 1 1} +} {INSERT main t1 1 1 x y} # 7.1.2.1 does not use the xfer optimization. 7.1.2.2 does. do_preupdate_test 7.1.2.1 { INSERT INTO t1 SELECT y, x FROM t2; -} {INSERT main t1 2 2 INSERT main t1 3 3} +} {INSERT main t1 2 2 b a INSERT main t1 3 3 d c} do_preupdate_test 7.1.2.2 { INSERT INTO t1 SELECT * FROM t2; -} {INSERT main t1 4 4 INSERT main t1 5 5} +} {INSERT main t1 4 4 a b INSERT main t1 5 5 c d} do_preupdate_test 7.1.3 { REPLACE INTO t1(rowid, a, b) VALUES(1, 1, 1); } { DELETE main t1 1 1 x y - INSERT main t1 1 1 + INSERT main t1 1 1 1 1 } do_preupdate_test 7.1.4 { REPLACE INTO t3 VALUES(4, NULL); } { DELETE main t3 1 1 4 16 - INSERT main t3 4 4 + INSERT main t3 4 4 4 {} } do_preupdate_test 7.1.5 { @@ -462,7 +468,7 @@ do_preupdate_test 7.1.5 { } { DELETE main t3 2 2 5 25 DELETE main t3 3 3 6 36 - INSERT main t3 2 2 + INSERT main t3 2 2 6 {} } do_execsql_test 7.2.0 { SELECT rowid FROM t1 } {1 2 3 4 5} @@ -497,29 +503,29 @@ do_execsql_test 7.3.0 { do_preupdate_test 7.3.1 { UPDATE t2 SET y = y||y; } { - UPDATE main t2 1 1 a b - UPDATE main t2 2 2 c d + UPDATE main t2 1 1 a b a bb + UPDATE main t2 2 2 c d c dd } do_preupdate_test 7.3.2 { UPDATE t2 SET rowid = rowid-1; } { - UPDATE main t2 1 0 a bb - UPDATE main t2 2 1 c dd + UPDATE main t2 1 0 a bb a bb + UPDATE main t2 2 1 c dd c dd } do_preupdate_test 7.3.3 { UPDATE OR REPLACE t2 SET rowid = 1 WHERE x = 'a' } { DELETE main t2 1 1 c dd - UPDATE main t2 0 1 a bb + UPDATE main t2 0 1 a bb a bb } do_preupdate_test 7.3.4.1 { UPDATE OR REPLACE t3 SET i = 5 WHERE i = 6 } { DELETE main t3 2 2 5 25 - UPDATE main t3 3 3 6 36 + UPDATE main t3 3 3 6 36 5 36 } do_execsql_test 7.3.4.2 { @@ -532,7 +538,7 @@ do_preupdate_test 7.3.5 { } { DELETE main t3 1 1 4 16 DELETE main t3 3 3 5 36 - UPDATE main t3 4 1 10 100 + UPDATE main t3 4 1 10 100 5 100 } do_execsql_test 7.4.1.0 { @@ -577,7 +583,7 @@ do_preupdate_test 7.4.2.1 { UPDATE t5 SET b = 4 WHERE a = 'c' } { DELETE main t5 1 1 a 1 - UPDATE main t5 3 3 c 3 + UPDATE main t5 3 3 c 3 c 4 } do_execsql_test 7.4.2.2 { @@ -606,7 +612,7 @@ do_preupdate_test 7.5.1.1 { do_preupdate_test 7.5.1.2 { UPDATE t7 SET b = 'five' } { - UPDATE main t7 2 2 three four {} + UPDATE main t7 2 2 three four {} three five {} } do_execsql_test 7.5.2.0 { @@ -628,7 +634,7 @@ do_preupdate_test 7.5.2.1 { do_preupdate_test 7.5.2.2 { UPDATE t8 SET b = 'five' } { - UPDATE main t8 2 2 three four xxx + UPDATE main t8 2 2 three four xxx three five xxx } finish_test