Experimental change to use a single-pass approach for DELETE statements on non-virtual tables that do not fire triggers or require foriegn-key processing.
FossilOrigin-Name: eaeb2b80f6f8f83679c8323a81bb39570ec946fe
This commit is contained in:
parent
04abf0878f
commit
f0ee1d3c12
36
manifest
36
manifest
@ -1,5 +1,5 @@
|
|||||||
C Import\scommon\schanges\sfrom\sthe\smutex\sinitialization\sbranch.
|
C Experimental\schange\sto\suse\sa\ssingle-pass\sapproach\sfor\sDELETE\sstatements\son\snon-virtual\stables\sthat\sdo\snot\sfire\striggers\sor\srequire\sforiegn-key\sprocessing.
|
||||||
D 2015-09-12T18:57:45.818
|
D 2015-09-12T19:26:11.066
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
|
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@ -282,8 +282,8 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
|||||||
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
|
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
|
||||||
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
|
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
|
||||||
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||||
F src/btree.c 4084d9eed2817331f6e6a82230ba30e448cad497
|
F src/btree.c 38ed0262d1c66d21bb084f086650e6106ae43d98
|
||||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
F src/btree.h 40189aefdc2b830d25c8b58fd7d56538481bfdd7
|
||||||
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
|
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
|
||||||
F src/build.c f81380bc4d5d239c18b42982a9866a94489fd444
|
F src/build.c f81380bc4d5d239c18b42982a9866a94489fd444
|
||||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||||
@ -291,7 +291,7 @@ F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
|
|||||||
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||||
F src/date.c fb1c99172017dcc8e237339132c91a21a0788584
|
F src/date.c fb1c99172017dcc8e237339132c91a21a0788584
|
||||||
F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7
|
F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7
|
||||||
F src/delete.c 6792c80d7fb54c4df9f7680413952600e7439492
|
F src/delete.c d5a2dc4a4663225abbcab042478dc37a5749b7d7
|
||||||
F src/expr.c 3a76afcdac925294c39903b7002ddb9e5fd29863
|
F src/expr.c 3a76afcdac925294c39903b7002ddb9e5fd29863
|
||||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||||
F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f
|
F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f
|
||||||
@ -300,7 +300,7 @@ F src/global.c 508e4087f7b41d688e4762dcf4d4fe28cfbc87f9
|
|||||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||||
F src/insert.c 076dc5876e261a9908603d54cfc5344cd680166c
|
F src/insert.c db8a34cf8ba600ac1cebb3c03e93c92154d0fc4c
|
||||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||||
F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012
|
F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012
|
||||||
@ -345,7 +345,7 @@ F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42
|
|||||||
F src/sqlite.h.in dbaf8c3796e80221de4395b5f4f872abddb5f89f
|
F src/sqlite.h.in dbaf8c3796e80221de4395b5f4f872abddb5f89f
|
||||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||||
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
|
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
|
||||||
F src/sqliteInt.h b3e590f374b376a793b93e2387b8d5aca0fc92c4
|
F src/sqliteInt.h 91bf09de55402157d1476a61df46ef6cfbc0bbc3
|
||||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||||
@ -399,11 +399,11 @@ F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
|
|||||||
F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6
|
F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6
|
||||||
F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce
|
F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce
|
||||||
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
|
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
|
||||||
F src/update.c 3c5bc9570df3bfafa0db36828406a8a14e4c426e
|
F src/update.c eb7ab3ff2928628692a4f14be397c95f4a681d97
|
||||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||||
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
|
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
|
||||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||||
F src/vdbe.c 6d85be995bd2308a5aa2a68c7b564c5d4cc1a6fb
|
F src/vdbe.c 5587d76bd5a4fb4f7ca023f161a6c6adbb1de26c
|
||||||
F src/vdbe.h 4bc88bd0e06f8046ee6ab7487c0015e85ad949ad
|
F src/vdbe.h 4bc88bd0e06f8046ee6ab7487c0015e85ad949ad
|
||||||
F src/vdbeInt.h 8b867eac234e28627ffcace3cd4b4b79bbec664b
|
F src/vdbeInt.h 8b867eac234e28627ffcace3cd4b4b79bbec664b
|
||||||
F src/vdbeapi.c 0d890f57caf143b114a95ce699e59af51359c508
|
F src/vdbeapi.c 0d890f57caf143b114a95ce699e59af51359c508
|
||||||
@ -417,7 +417,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
|||||||
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
|
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
|
||||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||||
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
||||||
F src/where.c 1227687e7892d4009f3c3433e974eb9c9e3c4d6a
|
F src/where.c 98cbedead64380fc26a098350f43d92237c8fa17
|
||||||
F src/whereInt.h 292d3ac90da4eab1e03ac8452f1add746bcafaa1
|
F src/whereInt.h 292d3ac90da4eab1e03ac8452f1add746bcafaa1
|
||||||
F src/wherecode.c 6ac8599523f4840d9efac335329f627ebf3f79fd
|
F src/wherecode.c 6ac8599523f4840d9efac335329f627ebf3f79fd
|
||||||
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
|
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
|
||||||
@ -569,9 +569,10 @@ F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0
|
|||||||
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
|
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
|
||||||
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
|
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
|
||||||
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
||||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f
|
||||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||||
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
|
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
|
||||||
|
F test/delete4.test d9e7d553a939597b27d205b022d769469f361c1f
|
||||||
F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||||
@ -782,7 +783,7 @@ F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
|||||||
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
|
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
|
||||||
F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a
|
F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a
|
||||||
F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c
|
F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c
|
||||||
F test/indexedby.test 69d2292dfdabe85aa7c5df577c71bb4325607ec2
|
F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985
|
||||||
F test/indexexpr1.test 4feec154aadacb033b41acc1760a18edc4c60470
|
F test/indexexpr1.test 4feec154aadacb033b41acc1760a18edc4c60470
|
||||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||||
@ -1386,7 +1387,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
P 86781093bdb4c4fdedd228cb1c8961db48a483bb
|
P 334720c01722478af0d3dfd6fe8bafd88ba09f49
|
||||||
R 65cef7a212785e2c4ed01bbc55ee07c1
|
R 7f7d9f32d96ad1f6004a92150fc6799e
|
||||||
U mistachkin
|
T *branch * onepass-delete
|
||||||
Z 122b4ea017ab904cde327a85d32df791
|
T *sym-onepass-delete *
|
||||||
|
T -sym-trunk *
|
||||||
|
U dan
|
||||||
|
Z 9a0d15a0b8fdf4a99fe6f2640c27db22
|
||||||
|
@ -1 +1 @@
|
|||||||
334720c01722478af0d3dfd6fe8bafd88ba09f49
|
eaeb2b80f6f8f83679c8323a81bb39570ec946fe
|
126
src/btree.c
126
src/btree.c
@ -591,6 +591,49 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
|
|||||||
pCur->iPage = -1;
|
pCur->iPage = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The cursor passed as the only argument must point to a valid entry
|
||||||
|
** when this function is called (i.e. have eState==CURSOR_VALID). This
|
||||||
|
** function saves the current cursor key in variables pCur->nKey and
|
||||||
|
** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
|
||||||
|
** code otherwise.
|
||||||
|
**
|
||||||
|
** If the cursor is open on an intkey table, then the integer key
|
||||||
|
** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
|
||||||
|
** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
|
||||||
|
** set to point to a malloced buffer pCur->nKey bytes in size containing
|
||||||
|
** the key.
|
||||||
|
*/
|
||||||
|
static int saveCursorKey(BtCursor *pCur){
|
||||||
|
int rc;
|
||||||
|
assert( CURSOR_VALID==pCur->eState );
|
||||||
|
assert( 0==pCur->pKey );
|
||||||
|
assert( cursorHoldsMutex(pCur) );
|
||||||
|
|
||||||
|
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
|
||||||
|
assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
|
||||||
|
|
||||||
|
/* If this is an intKey table, then the above call to BtreeKeySize()
|
||||||
|
** stores the integer key in pCur->nKey. In this case this value is
|
||||||
|
** all that is required. Otherwise, if pCur is not open on an intKey
|
||||||
|
** table, then malloc space for and store the pCur->nKey bytes of key
|
||||||
|
** data. */
|
||||||
|
if( 0==pCur->curIntKey ){
|
||||||
|
void *pKey = sqlite3Malloc( pCur->nKey );
|
||||||
|
if( pKey ){
|
||||||
|
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
||||||
|
if( rc==SQLITE_OK ){
|
||||||
|
pCur->pKey = pKey;
|
||||||
|
}else{
|
||||||
|
sqlite3_free(pKey);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
rc = SQLITE_NOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert( !pCur->curIntKey || !pCur->pKey );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Save the current cursor position in the variables BtCursor.nKey
|
** Save the current cursor position in the variables BtCursor.nKey
|
||||||
@ -611,30 +654,8 @@ static int saveCursorPosition(BtCursor *pCur){
|
|||||||
}else{
|
}else{
|
||||||
pCur->skipNext = 0;
|
pCur->skipNext = 0;
|
||||||
}
|
}
|
||||||
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
|
|
||||||
assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
|
|
||||||
|
|
||||||
/* If this is an intKey table, then the above call to BtreeKeySize()
|
|
||||||
** stores the integer key in pCur->nKey. In this case this value is
|
|
||||||
** all that is required. Otherwise, if pCur is not open on an intKey
|
|
||||||
** table, then malloc space for and store the pCur->nKey bytes of key
|
|
||||||
** data.
|
|
||||||
*/
|
|
||||||
if( 0==pCur->curIntKey ){
|
|
||||||
void *pKey = sqlite3Malloc( pCur->nKey );
|
|
||||||
if( pKey ){
|
|
||||||
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
|
||||||
if( rc==SQLITE_OK ){
|
|
||||||
pCur->pKey = pKey;
|
|
||||||
}else{
|
|
||||||
sqlite3_free(pKey);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
rc = SQLITE_NOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert( !pCur->curIntKey || !pCur->pKey );
|
|
||||||
|
|
||||||
|
rc = saveCursorKey(pCur);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
btreeReleaseAllCursorPages(pCur);
|
btreeReleaseAllCursorPages(pCur);
|
||||||
pCur->eState = CURSOR_REQUIRESEEK;
|
pCur->eState = CURSOR_REQUIRESEEK;
|
||||||
@ -8026,10 +8047,15 @@ end_insert:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Delete the entry that the cursor is pointing to. The cursor
|
** Delete the entry that the cursor is pointing to.
|
||||||
** is left pointing at an arbitrary location.
|
**
|
||||||
|
** If the second parameter is zero, then the cursor is left pointing at an
|
||||||
|
** arbitrary location after the delete. If it is non-zero, then the cursor
|
||||||
|
** is left in a state such that the next call to BtreeNext() or BtreePrev()
|
||||||
|
** moves it to the same row as it would if the call to BtreeDelete() had
|
||||||
|
** been omitted.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeDelete(BtCursor *pCur){
|
int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
|
||||||
Btree *p = pCur->pBtree;
|
Btree *p = pCur->pBtree;
|
||||||
BtShared *pBt = p->pBt;
|
BtShared *pBt = p->pBt;
|
||||||
int rc; /* Return code */
|
int rc; /* Return code */
|
||||||
@ -8038,6 +8064,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
int iCellIdx; /* Index of cell to delete */
|
int iCellIdx; /* Index of cell to delete */
|
||||||
int iCellDepth; /* Depth of node containing pCell */
|
int iCellDepth; /* Depth of node containing pCell */
|
||||||
u16 szCell; /* Size of the cell being deleted */
|
u16 szCell; /* Size of the cell being deleted */
|
||||||
|
int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
|
||||||
|
|
||||||
assert( cursorHoldsMutex(pCur) );
|
assert( cursorHoldsMutex(pCur) );
|
||||||
assert( pBt->inTransaction==TRANS_WRITE );
|
assert( pBt->inTransaction==TRANS_WRITE );
|
||||||
@ -8067,10 +8094,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Save the positions of any other cursors open on this table before
|
/* Save the positions of any other cursors open on this table before
|
||||||
** making any modifications. Make the page containing the entry to be
|
** making any modifications. */
|
||||||
** deleted writable. Then free any overflow pages associated with the
|
|
||||||
** entry and finally remove the cell itself from within the page.
|
|
||||||
*/
|
|
||||||
if( pCur->curFlags & BTCF_Multiple ){
|
if( pCur->curFlags & BTCF_Multiple ){
|
||||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
@ -8082,6 +8106,31 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the bPreserve flag is set to true, then the cursor position must
|
||||||
|
** be preserved following this delete operation. If the current delete
|
||||||
|
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||||
|
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||||
|
** returning.
|
||||||
|
**
|
||||||
|
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||||
|
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||||
|
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||||
|
if( bPreserve ){
|
||||||
|
if( !pPage->leaf
|
||||||
|
|| (pPage->nFree + cellSizePtr(pPage, pCell) + 2)>(pBt->usableSize*2/3)
|
||||||
|
){
|
||||||
|
/* A b-tree rebalance will be required after deleting this entry.
|
||||||
|
** Save the cursor key. */
|
||||||
|
rc = saveCursorKey(pCur);
|
||||||
|
if( rc ) return rc;
|
||||||
|
}else{
|
||||||
|
bSkipnext = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make the page containing the entry to be deleted writable. Then free any
|
||||||
|
** overflow pages associated with the entry and finally remove the cell
|
||||||
|
** itself from within the page. */
|
||||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
rc = clearCell(pPage, pCell, &szCell);
|
rc = clearCell(pPage, pCell, &szCell);
|
||||||
@ -8135,7 +8184,22 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
moveToRoot(pCur);
|
if( bSkipnext ){
|
||||||
|
assert( bPreserve && pCur->iPage==iCellDepth );
|
||||||
|
assert( pPage->nCell>0 && iCellIdx<=pPage->nCell );
|
||||||
|
pCur->eState = CURSOR_SKIPNEXT;
|
||||||
|
if( iCellIdx>=pPage->nCell ){
|
||||||
|
pCur->skipNext = -1;
|
||||||
|
pCur->aiIdx[iCellDepth] = pPage->nCell-1;
|
||||||
|
}else{
|
||||||
|
pCur->skipNext = 1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
rc = moveToRoot(pCur);
|
||||||
|
if( bPreserve ){
|
||||||
|
pCur->eState = CURSOR_REQUIRESEEK;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ int sqlite3BtreeMovetoUnpacked(
|
|||||||
);
|
);
|
||||||
int sqlite3BtreeCursorHasMoved(BtCursor*);
|
int sqlite3BtreeCursorHasMoved(BtCursor*);
|
||||||
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
||||||
int sqlite3BtreeDelete(BtCursor*);
|
int sqlite3BtreeDelete(BtCursor*, int);
|
||||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||||
const void *pData, int nData,
|
const void *pData, int nData,
|
||||||
int nZero, int bias, int seekResult);
|
int nZero, int bias, int seekResult);
|
||||||
|
100
src/delete.c
100
src/delete.c
@ -235,7 +235,7 @@ void sqlite3DeleteFrom(
|
|||||||
int iDb; /* Database number */
|
int iDb; /* Database number */
|
||||||
int memCnt = -1; /* Memory cell used for change counting */
|
int memCnt = -1; /* Memory cell used for change counting */
|
||||||
int rcauth; /* Value returned by authorization callback */
|
int rcauth; /* Value returned by authorization callback */
|
||||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
int eOnePass; /* Non-zero for one-pass algorithm without the FIFO */
|
||||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||||
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
|
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
|
||||||
Index *pPk; /* The PRIMARY KEY index on the table */
|
Index *pPk; /* The PRIMARY KEY index on the table */
|
||||||
@ -253,6 +253,7 @@ void sqlite3DeleteFrom(
|
|||||||
#ifndef SQLITE_OMIT_TRIGGER
|
#ifndef SQLITE_OMIT_TRIGGER
|
||||||
int isView; /* True if attempting to delete from a view */
|
int isView; /* True if attempting to delete from a view */
|
||||||
Trigger *pTrigger; /* List of table triggers, if required */
|
Trigger *pTrigger; /* List of table triggers, if required */
|
||||||
|
int bComplex; /* True if there are either triggers or FKs */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(&sContext, 0, sizeof(sContext));
|
memset(&sContext, 0, sizeof(sContext));
|
||||||
@ -276,9 +277,11 @@ void sqlite3DeleteFrom(
|
|||||||
#ifndef SQLITE_OMIT_TRIGGER
|
#ifndef SQLITE_OMIT_TRIGGER
|
||||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||||
isView = pTab->pSelect!=0;
|
isView = pTab->pSelect!=0;
|
||||||
|
bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||||
#else
|
#else
|
||||||
# define pTrigger 0
|
# define pTrigger 0
|
||||||
# define isView 0
|
# define isView 0
|
||||||
|
# define bComplex 0
|
||||||
#endif
|
#endif
|
||||||
#ifdef SQLITE_OMIT_VIEW
|
#ifdef SQLITE_OMIT_VIEW
|
||||||
# undef isView
|
# undef isView
|
||||||
@ -359,9 +362,7 @@ void sqlite3DeleteFrom(
|
|||||||
** It is easier just to erase the whole table. Prior to version 3.6.5,
|
** It is easier just to erase the whole table. Prior to version 3.6.5,
|
||||||
** this optimization caused the row change count (the value returned by
|
** this optimization caused the row change count (the value returned by
|
||||||
** API function sqlite3_count_changes) to be set incorrectly. */
|
** API function sqlite3_count_changes) to be set incorrectly. */
|
||||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
|
if( rcauth==SQLITE_OK && pWhere==0 && !bComplex && !IsVirtual(pTab) ){
|
||||||
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
|
|
||||||
){
|
|
||||||
assert( !isView );
|
assert( !isView );
|
||||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||||
if( HasRowid(pTab) ){
|
if( HasRowid(pTab) ){
|
||||||
@ -375,6 +376,8 @@ void sqlite3DeleteFrom(
|
|||||||
}else
|
}else
|
||||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||||
{
|
{
|
||||||
|
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||||
|
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||||
if( HasRowid(pTab) ){
|
if( HasRowid(pTab) ){
|
||||||
/* For a rowid table, initialize the RowSet to an empty set */
|
/* For a rowid table, initialize the RowSet to an empty set */
|
||||||
pPk = 0;
|
pPk = 0;
|
||||||
@ -395,13 +398,17 @@ void sqlite3DeleteFrom(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Construct a query to find the rowid or primary key for every row
|
/* Construct a query to find the rowid or primary key for every row
|
||||||
** to be deleted, based on the WHERE clause.
|
** to be deleted, based on the WHERE clause. Set variable eOnePass
|
||||||
|
** to indicate the strategy used to implement this delete:
|
||||||
|
**
|
||||||
|
** 0: Two-pass approach - use a FIFO for rowids/PK values.
|
||||||
|
** 1: One-pass approach - at most one row deleted.
|
||||||
|
** 2: One-pass approach - any number of rows may be deleted.
|
||||||
*/
|
*/
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
|
||||||
WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
|
|
||||||
iTabCur+1);
|
|
||||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||||
|
assert( IsVirtual(pTab)==0 || eOnePass==0 );
|
||||||
|
|
||||||
/* Keep track of the number of rows to be deleted */
|
/* Keep track of the number of rows to be deleted */
|
||||||
if( db->flags & SQLITE_CountRows ){
|
if( db->flags & SQLITE_CountRows ){
|
||||||
@ -422,11 +429,10 @@ void sqlite3DeleteFrom(
|
|||||||
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( okOnePass ){
|
if( eOnePass ){
|
||||||
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
||||||
** one, so just keep it in its register(s) and fall through to the
|
** one, so just keep it in its register(s) and fall through to the
|
||||||
** delete code.
|
** delete code. */
|
||||||
*/
|
|
||||||
nKey = nPk; /* OP_Found will use an unpacked key */
|
nKey = nPk; /* OP_Found will use an unpacked key */
|
||||||
aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
|
aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
|
||||||
if( aToOpen==0 ){
|
if( aToOpen==0 ){
|
||||||
@ -438,27 +444,27 @@ void sqlite3DeleteFrom(
|
|||||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
||||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
||||||
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
||||||
addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
|
}else{
|
||||||
}else if( pPk ){
|
if( pPk ){
|
||||||
/* Construct a composite key for the row to be deleted and remember it */
|
/* Add the PK key for this row to the temporary table */
|
||||||
iKey = ++pParse->nMem;
|
iKey = ++pParse->nMem;
|
||||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||||
}else{
|
}else{
|
||||||
/* Get the rowid of the row to be deleted and remember it in the RowSet */
|
/* Add the rowid of the row to be deleted to the RowSet */
|
||||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* End of the WHERE loop */
|
/* If this DELETE cannot use the ONEPASS strategy, this is the
|
||||||
sqlite3WhereEnd(pWInfo);
|
** end of the WHERE loop */
|
||||||
if( okOnePass ){
|
if( eOnePass ){
|
||||||
/* Bypass the delete logic below if the WHERE loop found zero rows */
|
|
||||||
addrBypass = sqlite3VdbeMakeLabel(v);
|
addrBypass = sqlite3VdbeMakeLabel(v);
|
||||||
sqlite3VdbeGoto(v, addrBypass);
|
}else{
|
||||||
sqlite3VdbeJumpHere(v, addrDelete);
|
sqlite3WhereEnd(pWInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unless this is a view, open cursors for the table we are
|
/* Unless this is a view, open cursors for the table we are
|
||||||
@ -467,20 +473,21 @@ void sqlite3DeleteFrom(
|
|||||||
** triggers.
|
** triggers.
|
||||||
*/
|
*/
|
||||||
if( !isView ){
|
if( !isView ){
|
||||||
|
int iAddrOnce;
|
||||||
|
if( eOnePass==2 ) iAddrOnce = sqlite3CodeOnce(pParse);
|
||||||
testcase( IsVirtual(pTab) );
|
testcase( IsVirtual(pTab) );
|
||||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
|
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
|
||||||
&iDataCur, &iIdxCur);
|
&iDataCur, &iIdxCur);
|
||||||
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
|
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
|
||||||
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
|
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
|
||||||
|
if( eOnePass==2 ) sqlite3VdbeJumpHere(v, iAddrOnce);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up a loop over the rowids/primary-keys that were found in the
|
/* Set up a loop over the rowids/primary-keys that were found in the
|
||||||
** where-clause loop above.
|
** where-clause loop above.
|
||||||
*/
|
*/
|
||||||
if( okOnePass ){
|
if( eOnePass ){
|
||||||
/* Just one row. Hence the top-of-loop is a no-op */
|
|
||||||
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
||||||
assert( !IsVirtual(pTab) );
|
|
||||||
if( aToOpen[iDataCur-iTabCur] ){
|
if( aToOpen[iDataCur-iTabCur] ){
|
||||||
assert( pPk!=0 || pTab->pSelect!=0 );
|
assert( pPk!=0 || pTab->pSelect!=0 );
|
||||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
||||||
@ -508,13 +515,18 @@ void sqlite3DeleteFrom(
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
int count = (pParse->nested==0); /* True to count changes */
|
int count = (pParse->nested==0); /* True to count changes */
|
||||||
|
int iIdxNoSeek = -1;
|
||||||
|
if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){
|
||||||
|
iIdxNoSeek = aiCurOnePass[1];
|
||||||
|
}
|
||||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||||
iKey, nKey, count, OE_Default, okOnePass);
|
iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End of the loop over all rowids/primary-keys. */
|
/* End of the loop over all rowids/primary-keys. */
|
||||||
if( okOnePass ){
|
if( eOnePass ){
|
||||||
sqlite3VdbeResolveLabel(v, addrBypass);
|
sqlite3VdbeResolveLabel(v, addrBypass);
|
||||||
|
sqlite3WhereEnd(pWInfo);
|
||||||
}else if( pPk ){
|
}else if( pPk ){
|
||||||
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
|
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
|
||||||
sqlite3VdbeJumpHere(v, addrLoop);
|
sqlite3VdbeJumpHere(v, addrLoop);
|
||||||
@ -586,6 +598,25 @@ delete_from_cleanup:
|
|||||||
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
|
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
|
||||||
** that a search record formed from OP_MakeRecord is contained in the
|
** that a search record formed from OP_MakeRecord is contained in the
|
||||||
** single memory location iPk.
|
** single memory location iPk.
|
||||||
|
**
|
||||||
|
** eMode:
|
||||||
|
** Parameter eMode may be passed either 0, 1 or 2. If it is passed a
|
||||||
|
** non-zero value, then it is guaranteed that cursor iDataCur already
|
||||||
|
** points to the row to delete. If it is passed 0, then this function
|
||||||
|
** must seek iDataCur to the entry identified by iPk and nPk before
|
||||||
|
** reading from it.
|
||||||
|
**
|
||||||
|
** If eMode is passed the value 2, then this call is being made as part
|
||||||
|
** of a ONEPASS delete that affects multiple rows. In this case, if
|
||||||
|
** iIdxNoSeek is a valid cursor number (>=0), then its position should
|
||||||
|
** be preserved following the delete operation. Or, if iIdxNoSeek is not
|
||||||
|
** a valid cursor number, the position of iDataCur should be preserved
|
||||||
|
** instead.
|
||||||
|
**
|
||||||
|
** iIdxNoSeek:
|
||||||
|
** If iIdxNoSeek is a valid cursor number (>=0), then it identifies an
|
||||||
|
** index cursor (from within array of cursors starting at iIdxCur) that
|
||||||
|
** already points to the index entry to be deleted.
|
||||||
*/
|
*/
|
||||||
void sqlite3GenerateRowDelete(
|
void sqlite3GenerateRowDelete(
|
||||||
Parse *pParse, /* Parsing context */
|
Parse *pParse, /* Parsing context */
|
||||||
@ -597,7 +628,8 @@ void sqlite3GenerateRowDelete(
|
|||||||
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
||||||
u8 count, /* If non-zero, increment the row change counter */
|
u8 count, /* If non-zero, increment the row change counter */
|
||||||
u8 onconf, /* Default ON CONFLICT policy for triggers */
|
u8 onconf, /* Default ON CONFLICT policy for triggers */
|
||||||
u8 bNoSeek /* iDataCur is already pointing to the row to delete */
|
u8 eMode, /* See explanation above */
|
||||||
|
int iIdxNoSeek /* Cursor number of cursor that does not need seeking */
|
||||||
){
|
){
|
||||||
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
||||||
int iOld = 0; /* First register in OLD.* array */
|
int iOld = 0; /* First register in OLD.* array */
|
||||||
@ -614,7 +646,7 @@ void sqlite3GenerateRowDelete(
|
|||||||
** not attempt to delete it or fire any DELETE triggers. */
|
** not attempt to delete it or fire any DELETE triggers. */
|
||||||
iLabel = sqlite3VdbeMakeLabel(v);
|
iLabel = sqlite3VdbeMakeLabel(v);
|
||||||
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||||
if( !bNoSeek ){
|
if( eMode==0 ){
|
||||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||||
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
||||||
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
||||||
@ -674,11 +706,15 @@ void sqlite3GenerateRowDelete(
|
|||||||
** a view (in which case the only effect of the DELETE statement is to
|
** a view (in which case the only effect of the DELETE statement is to
|
||||||
** fire the INSTEAD OF triggers). */
|
** fire the INSTEAD OF triggers). */
|
||||||
if( pTab->pSelect==0 ){
|
if( pTab->pSelect==0 ){
|
||||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
||||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||||
if( count ){
|
if( count ){
|
||||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||||
}
|
}
|
||||||
|
if( iIdxNoSeek>=0 ){
|
||||||
|
sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
|
||||||
|
}
|
||||||
|
sqlite3VdbeChangeP5(v, eMode==2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||||
@ -721,7 +757,8 @@ void sqlite3GenerateRowIndexDelete(
|
|||||||
Table *pTab, /* Table containing the row to be deleted */
|
Table *pTab, /* Table containing the row to be deleted */
|
||||||
int iDataCur, /* Cursor of table holding data. */
|
int iDataCur, /* Cursor of table holding data. */
|
||||||
int iIdxCur, /* First index cursor */
|
int iIdxCur, /* First index cursor */
|
||||||
int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
int *aRegIdx, /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||||
|
int iIdxNoSeek /* Do not delete from this cursor */
|
||||||
){
|
){
|
||||||
int i; /* Index loop counter */
|
int i; /* Index loop counter */
|
||||||
int r1 = -1; /* Register holding an index key */
|
int r1 = -1; /* Register holding an index key */
|
||||||
@ -737,6 +774,7 @@ void sqlite3GenerateRowIndexDelete(
|
|||||||
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
||||||
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
||||||
if( pIdx==pPk ) continue;
|
if( pIdx==pPk ) continue;
|
||||||
|
if( iIdxCur+i==iIdxNoSeek ) continue;
|
||||||
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
|
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
|
||||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
|
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
|
||||||
&iPartIdxLabel, pPrior, r1);
|
&iPartIdxLabel, pPrior, r1);
|
||||||
|
@ -1347,10 +1347,10 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||||
sqlite3MultiWrite(pParse);
|
sqlite3MultiWrite(pParse);
|
||||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||||
regNewData, 1, 0, OE_Replace, 1);
|
regNewData, 1, 0, OE_Replace, 1, -1);
|
||||||
}else if( pTab->pIndex ){
|
}else if( pTab->pIndex ){
|
||||||
sqlite3MultiWrite(pParse);
|
sqlite3MultiWrite(pParse);
|
||||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0, -1);
|
||||||
}
|
}
|
||||||
seenReplace = 1;
|
seenReplace = 1;
|
||||||
break;
|
break;
|
||||||
@ -1528,7 +1528,7 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||||
}
|
}
|
||||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||||
regR, nPkField, 0, OE_Replace, pIdx==pPk);
|
regR, nPkField, 0, OE_Replace, pIdx==pPk, -1);
|
||||||
seenReplace = 1;
|
seenReplace = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2352,6 +2352,7 @@ struct SrcList {
|
|||||||
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
|
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
|
||||||
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
|
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
|
||||||
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
|
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
|
||||||
|
#define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */
|
||||||
|
|
||||||
/* Allowed return values from sqlite3WhereIsDistinct()
|
/* Allowed return values from sqlite3WhereIsDistinct()
|
||||||
*/
|
*/
|
||||||
@ -3440,8 +3441,9 @@ int sqlite3ExprIsInteger(Expr*, int*);
|
|||||||
int sqlite3ExprCanBeNull(const Expr*);
|
int sqlite3ExprCanBeNull(const Expr*);
|
||||||
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
||||||
int sqlite3IsRowid(const char*);
|
int sqlite3IsRowid(const char*);
|
||||||
void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
|
void sqlite3GenerateRowDelete(
|
||||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
|
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
|
||||||
|
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
|
||||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
||||||
void sqlite3ResolvePartIdxLabel(Parse*,int);
|
void sqlite3ResolvePartIdxLabel(Parse*,int);
|
||||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
||||||
|
@ -587,7 +587,7 @@ void sqlite3Update(
|
|||||||
}
|
}
|
||||||
VdbeCoverageNeverTaken(v);
|
VdbeCoverageNeverTaken(v);
|
||||||
}
|
}
|
||||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
|
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
|
||||||
|
|
||||||
/* If changing the record number, delete the old record. */
|
/* If changing the record number, delete the old record. */
|
||||||
if( hasFK || chngKey || pPk!=0 ){
|
if( hasFK || chngKey || pPk!=0 ){
|
||||||
|
25
src/vdbe.c
25
src/vdbe.c
@ -4274,14 +4274,15 @@ case OP_InsertInt: {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opcode: Delete P1 P2 * P4 *
|
/* Opcode: Delete P1 P2 * P4 P5
|
||||||
**
|
**
|
||||||
** Delete the record at which the P1 cursor is currently pointing.
|
** Delete the record at which the P1 cursor is currently pointing.
|
||||||
**
|
**
|
||||||
** The cursor will be left pointing at either the next or the previous
|
** If the P5 parameter is non-zero, the cursor will be left pointing at
|
||||||
** record in the table. If it is left pointing at the next record, then
|
** either the next or the previous record in the table. If it is left
|
||||||
** the next Next instruction will be a no-op. Hence it is OK to delete
|
** pointing at the next record, then the next Next instruction will be a
|
||||||
** a record from within a Next loop.
|
** no-op. As a result, in this case it is OK to delete a record from within a
|
||||||
|
** Next loop. If P5 is zero, then the cursor is left in an undefined state.
|
||||||
**
|
**
|
||||||
** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
|
** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
|
||||||
** incremented (otherwise not).
|
** incremented (otherwise not).
|
||||||
@ -4301,20 +4302,26 @@ case OP_Delete: {
|
|||||||
pC = p->apCsr[pOp->p1];
|
pC = p->apCsr[pOp->p1];
|
||||||
assert( pC!=0 );
|
assert( pC!=0 );
|
||||||
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
|
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
|
||||||
assert( pC->deferredMoveto==0 );
|
|
||||||
|
if( pC->deferredMoveto ){
|
||||||
|
rc = sqlite3VdbeCursorMoveto(pC);
|
||||||
|
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||||
|
}else if( pOp->p5 && db->xUpdateCallback && pOp->p4.z && pC->isTable ){
|
||||||
|
sqlite3BtreeKeySize(pC->pCursor, &pC->movetoTarget);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SQLITE_DEBUG
|
#ifdef SQLITE_DEBUG
|
||||||
/* The seek operation that positioned the cursor prior to OP_Delete will
|
/* The seek operation that positioned the cursor prior to OP_Delete will
|
||||||
** have also set the pC->movetoTarget field to the rowid of the row that
|
** have also set the pC->movetoTarget field to the rowid of the row that
|
||||||
** is being deleted */
|
** is being deleted */
|
||||||
if( pOp->p4.z && pC->isTable ){
|
if( pOp->p4.z && pC->isTable && pOp->p5==0 ){
|
||||||
i64 iKey = 0;
|
i64 iKey = 0;
|
||||||
sqlite3BtreeKeySize(pC->pCursor, &iKey);
|
sqlite3BtreeKeySize(pC->pCursor, &iKey);
|
||||||
assert( pC->movetoTarget==iKey );
|
assert( pC->movetoTarget==iKey );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rc = sqlite3BtreeDelete(pC->pCursor);
|
rc = sqlite3BtreeDelete(pC->pCursor, pOp->p5);
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
|
|
||||||
/* Invoke the update-hook if required. */
|
/* Invoke the update-hook if required. */
|
||||||
@ -4857,7 +4864,7 @@ case OP_IdxDelete: {
|
|||||||
#endif
|
#endif
|
||||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
|
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
|
||||||
if( rc==SQLITE_OK && res==0 ){
|
if( rc==SQLITE_OK && res==0 ){
|
||||||
rc = sqlite3BtreeDelete(pCrsr);
|
rc = sqlite3BtreeDelete(pCrsr, 0);
|
||||||
}
|
}
|
||||||
assert( pC->deferredMoveto==0 );
|
assert( pC->deferredMoveto==0 );
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
|
15
src/where.c
15
src/where.c
@ -3958,6 +3958,10 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
sqlite3 *db; /* Database connection */
|
sqlite3 *db; /* Database connection */
|
||||||
int rc; /* Return code */
|
int rc; /* Return code */
|
||||||
|
|
||||||
|
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
|
||||||
|
(wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||||
|
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||||
|
));
|
||||||
|
|
||||||
/* Variable initialization */
|
/* Variable initialization */
|
||||||
db = pParse->db;
|
db = pParse->db;
|
||||||
@ -4199,13 +4203,18 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
** the statement to update or delete a single row.
|
** the statement to update or delete a single row.
|
||||||
*/
|
*/
|
||||||
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
|
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
|
||||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
|
||||||
&& (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
|
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
|
||||||
pWInfo->okOnePass = 1;
|
int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
|
||||||
|
if( bOnerow || ( (wctrlFlags & WHERE_ONEPASS_MULTIROW)
|
||||||
|
&& 0==(wsFlags & WHERE_VIRTUALTABLE)
|
||||||
|
)){
|
||||||
|
pWInfo->okOnePass = bOnerow ? 1 : 2;
|
||||||
if( HasRowid(pTabList->a[0].pTab) ){
|
if( HasRowid(pTabList->a[0].pTab) ){
|
||||||
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Open all tables in the pTabList and any indices selected for
|
/* Open all tables in the pTabList and any indices selected for
|
||||||
** searching those tables.
|
** searching those tables.
|
||||||
|
@ -68,7 +68,6 @@ do_test delete-3.1.7 {
|
|||||||
} {1 2 4 16}
|
} {1 2 4 16}
|
||||||
integrity_check delete-3.2
|
integrity_check delete-3.2
|
||||||
|
|
||||||
|
|
||||||
# Semantic errors in the WHERE clause
|
# Semantic errors in the WHERE clause
|
||||||
#
|
#
|
||||||
do_test delete-4.1 {
|
do_test delete-4.1 {
|
||||||
|
102
test/delete4.test
Normal file
102
test/delete4.test
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# 2005 August 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 SQLite library. The
|
||||||
|
# focus of this script is a test of the DELETE command.
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set testprefix delete4
|
||||||
|
|
||||||
|
do_execsql_test 1.1 {
|
||||||
|
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||||
|
INSERT INTO t1 VALUES(1, 0);
|
||||||
|
INSERT INTO t1 VALUES(2, 1);
|
||||||
|
INSERT INTO t1 VALUES(3, 0);
|
||||||
|
INSERT INTO t1 VALUES(4, 1);
|
||||||
|
INSERT INTO t1 VALUES(5, 0);
|
||||||
|
INSERT INTO t1 VALUES(6, 1);
|
||||||
|
INSERT INTO t1 VALUES(7, 0);
|
||||||
|
INSERT INTO t1 VALUES(8, 1);
|
||||||
|
}
|
||||||
|
do_execsql_test 1.2 {
|
||||||
|
DELETE FROM t1 WHERE y=1;
|
||||||
|
}
|
||||||
|
do_execsql_test 1.3 {
|
||||||
|
SELECT x FROM t1;
|
||||||
|
} {1 3 5 7}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 2.1 {
|
||||||
|
CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z);
|
||||||
|
INSERT INTO t1 VALUES(1, 0, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(2, 1, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(3, 0, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(4, 1, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(5, 0, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(6, 1, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(7, 0, randomblob(200));
|
||||||
|
INSERT INTO t1 VALUES(8, 1, randomblob(200));
|
||||||
|
}
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
DELETE FROM t1 WHERE y=1;
|
||||||
|
}
|
||||||
|
do_execsql_test 2.3 {
|
||||||
|
SELECT x FROM t1;
|
||||||
|
} {1 3 5 7}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 3.1 {
|
||||||
|
CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
|
||||||
|
INSERT INTO t1 VALUES(1, 2);
|
||||||
|
INSERT INTO t1 VALUES(2, 4);
|
||||||
|
INSERT INTO t1 VALUES(1, 5);
|
||||||
|
DELETE FROM t1 WHERE a=1;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {2 4}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# DELETE statement that uses the OR optimization
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 3.1 {
|
||||||
|
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
|
||||||
|
CREATE INDEX i1a ON t1(a);
|
||||||
|
CREATE INDEX i1b ON t1(b);
|
||||||
|
INSERT INTO t1 VALUES(1, 'one', 'i');
|
||||||
|
INSERT INTO t1 VALUES(2, 'two', 'ii');
|
||||||
|
INSERT INTO t1 VALUES(3, 'three', 'iii');
|
||||||
|
INSERT INTO t1 VALUES(4, 'four', 'iv');
|
||||||
|
INSERT INTO t1 VALUES(5, 'one', 'i');
|
||||||
|
INSERT INTO t1 VALUES(6, 'two', 'ii');
|
||||||
|
INSERT INTO t1 VALUES(7, 'three', 'iii');
|
||||||
|
INSERT INTO t1 VALUES(8, 'four', 'iv');
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 3.2 {
|
||||||
|
DELETE FROM t1 WHERE a='two' OR b='iv';
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.3 {
|
||||||
|
SELECT i FROM t1 ORDER BY i;
|
||||||
|
} {1 3 5 7}
|
||||||
|
|
||||||
|
do_execsql_test 3.4 {
|
||||||
|
PRAGMA integrity_check;
|
||||||
|
} {ok}
|
||||||
|
|
||||||
|
|
||||||
|
finish_test
|
@ -231,13 +231,13 @@ do_execsql_test indexedby-6.2 {
|
|||||||
#
|
#
|
||||||
do_execsql_test indexedby-7.1 {
|
do_execsql_test indexedby-7.1 {
|
||||||
EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5
|
EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5
|
||||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
|
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||||
do_execsql_test indexedby-7.2 {
|
do_execsql_test indexedby-7.2 {
|
||||||
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
|
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
|
||||||
} {0 0 0 {SCAN TABLE t1}}
|
} {0 0 0 {SCAN TABLE t1}}
|
||||||
do_execsql_test indexedby-7.3 {
|
do_execsql_test indexedby-7.3 {
|
||||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5
|
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5
|
||||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
|
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||||
do_execsql_test indexedby-7.4 {
|
do_execsql_test indexedby-7.4 {
|
||||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
|
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
|
||||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||||
|
Loading…
Reference in New Issue
Block a user