Use a single-pass approach for DELETE statements on non-virtual tables that do not require trigger or foreign key processing.
FossilOrigin-Name: 8b93cc5937000535c35c763c9326507a19892a6e
This commit is contained in:
commit
e402dc57ae
36
manifest
36
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sstatic\sVFS\smutexes\sto\sthe\sprimary\sheader\sfile\scomments.
|
||||
D 2015-09-13T20:15:01.158
|
||||
C Use\sa\ssingle-pass\sapproach\sfor\sDELETE\sstatements\son\snon-virtual\stables\sthat\sdo\snot\srequire\strigger\sor\sforeign\skey\sprocessing.
|
||||
D 2015-09-14T11:09:58.319
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -282,8 +282,8 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
|
||||
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
|
||||
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||
F src/btree.c 4084d9eed2817331f6e6a82230ba30e448cad497
|
||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
||||
F src/btree.c 38ed0262d1c66d21bb084f086650e6106ae43d98
|
||||
F src/btree.h 40189aefdc2b830d25c8b58fd7d56538481bfdd7
|
||||
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
|
||||
F src/build.c f81380bc4d5d239c18b42982a9866a94489fd444
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
@ -291,7 +291,7 @@ F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
|
||||
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||
F src/date.c fb1c99172017dcc8e237339132c91a21a0788584
|
||||
F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7
|
||||
F src/delete.c 6792c80d7fb54c4df9f7680413952600e7439492
|
||||
F src/delete.c 18b9c93ef552adc12b7127707b10deefc0c1d451
|
||||
F src/expr.c 3a76afcdac925294c39903b7002ddb9e5fd29863
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f
|
||||
@ -300,7 +300,7 @@ F src/global.c 508e4087f7b41d688e4762dcf4d4fe28cfbc87f9
|
||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 076dc5876e261a9908603d54cfc5344cd680166c
|
||||
F src/insert.c db8a34cf8ba600ac1cebb3c03e93c92154d0fc4c
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012
|
||||
@ -345,7 +345,7 @@ F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42
|
||||
F src/sqlite.h.in 50f83145c6543000b7d27525ecaec59a23d8280b
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
|
||||
F src/sqliteInt.h b3e590f374b376a793b93e2387b8d5aca0fc92c4
|
||||
F src/sqliteInt.h 91bf09de55402157d1476a61df46ef6cfbc0bbc3
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
@ -399,11 +399,11 @@ F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
|
||||
F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6
|
||||
F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce
|
||||
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
|
||||
F src/update.c 3c5bc9570df3bfafa0db36828406a8a14e4c426e
|
||||
F src/update.c eb7ab3ff2928628692a4f14be397c95f4a681d97
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c 6d85be995bd2308a5aa2a68c7b564c5d4cc1a6fb
|
||||
F src/vdbe.c a8a5cb1126bf79104e00326abd6a7d22ac3bc4c3
|
||||
F src/vdbe.h 4bc88bd0e06f8046ee6ab7487c0015e85ad949ad
|
||||
F src/vdbeInt.h 8b867eac234e28627ffcace3cd4b4b79bbec664b
|
||||
F src/vdbeapi.c 0d890f57caf143b114a95ce699e59af51359c508
|
||||
@ -417,9 +417,9 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
||||
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
||||
F src/where.c 1227687e7892d4009f3c3433e974eb9c9e3c4d6a
|
||||
F src/where.c 98cbedead64380fc26a098350f43d92237c8fa17
|
||||
F src/whereInt.h 292d3ac90da4eab1e03ac8452f1add746bcafaa1
|
||||
F src/wherecode.c 6ac8599523f4840d9efac335329f627ebf3f79fd
|
||||
F src/wherecode.c 780cccf12a07ddc1ea0c6f6eb95895a3d8f79a6e
|
||||
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@ -569,9 +569,10 @@ F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0
|
||||
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
|
||||
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
|
||||
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
|
||||
F test/delete4.test d9e7d553a939597b27d205b022d769469f361c1f
|
||||
F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
@ -782,7 +783,7 @@ F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
||||
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
|
||||
F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a
|
||||
F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c
|
||||
F test/indexedby.test 69d2292dfdabe85aa7c5df577c71bb4325607ec2
|
||||
F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985
|
||||
F test/indexexpr1.test 4feec154aadacb033b41acc1760a18edc4c60470
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
@ -1386,7 +1387,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 786333e05a75406bbd0b5c9c7beb8f16022eabec
|
||||
R 1a3f3f98d74800359cb14e46ec6e0053
|
||||
U mistachkin
|
||||
Z 7262f2bc6346d75912550091a71aa522
|
||||
P 9a867d9fbe74fe2b33d55e32737a66e9a77b7466 166d5af8914c6954fb24a06e9686f194c0d0acde
|
||||
R c69d19f4812c097d21c7641aacc08fe2
|
||||
T +closed 166d5af8914c6954fb24a06e9686f194c0d0acde
|
||||
U dan
|
||||
Z 2b1ef5f5371b990363dc017742f1aae5
|
||||
|
@ -1 +1 @@
|
||||
9a867d9fbe74fe2b33d55e32737a66e9a77b7466
|
||||
8b93cc5937000535c35c763c9326507a19892a6e
|
126
src/btree.c
126
src/btree.c
@ -591,6 +591,49 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
|
||||
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
|
||||
@ -611,30 +654,8 @@ static int saveCursorPosition(BtCursor *pCur){
|
||||
}else{
|
||||
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 ){
|
||||
btreeReleaseAllCursorPages(pCur);
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
@ -8026,10 +8047,15 @@ end_insert:
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the entry that the cursor is pointing to. The cursor
|
||||
** is left pointing at an arbitrary location.
|
||||
** Delete the entry that the cursor is pointing to.
|
||||
**
|
||||
** 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;
|
||||
BtShared *pBt = p->pBt;
|
||||
int rc; /* Return code */
|
||||
@ -8038,6 +8064,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
int iCellIdx; /* Index of cell to delete */
|
||||
int iCellDepth; /* Depth of node containing pCell */
|
||||
u16 szCell; /* Size of the cell being deleted */
|
||||
int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
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
|
||||
** making any modifications. 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.
|
||||
*/
|
||||
** making any modifications. */
|
||||
if( pCur->curFlags & BTCF_Multiple ){
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
@ -8082,6 +8106,31 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
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);
|
||||
if( rc ) return rc;
|
||||
rc = clearCell(pPage, pCell, &szCell);
|
||||
@ -8135,7 +8184,22 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
);
|
||||
int sqlite3BtreeCursorHasMoved(BtCursor*);
|
||||
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeDelete(BtCursor*, int);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
int nZero, int bias, int seekResult);
|
||||
|
121
src/delete.c
121
src/delete.c
@ -235,7 +235,7 @@ void sqlite3DeleteFrom(
|
||||
int iDb; /* Database number */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
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 */
|
||||
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
|
||||
Index *pPk; /* The PRIMARY KEY index on the table */
|
||||
@ -247,12 +247,12 @@ void sqlite3DeleteFrom(
|
||||
int iRowSet = 0; /* Register for rowset of rows to delete */
|
||||
int addrBypass = 0; /* Address of jump over the delete logic */
|
||||
int addrLoop = 0; /* Top of the delete loop */
|
||||
int addrDelete = 0; /* Jump directly to the delete logic */
|
||||
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
Trigger *pTrigger; /* List of table triggers, if required */
|
||||
int bComplex; /* True if there are either triggers or FKs */
|
||||
#endif
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
@ -276,9 +276,11 @@ void sqlite3DeleteFrom(
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
#else
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
# define bComplex 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
@ -359,9 +361,7 @@ void sqlite3DeleteFrom(
|
||||
** 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
|
||||
** API function sqlite3_count_changes) to be set incorrectly. */
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
|
||||
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
|
||||
){
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !bComplex && !IsVirtual(pTab) ){
|
||||
assert( !isView );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
if( HasRowid(pTab) ){
|
||||
@ -375,6 +375,8 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
{
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||
if( HasRowid(pTab) ){
|
||||
/* For a rowid table, initialize the RowSet to an empty set */
|
||||
pPk = 0;
|
||||
@ -395,13 +397,17 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
|
||||
/* 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,
|
||||
WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
|
||||
iTabCur+1);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
|
||||
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 */
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
@ -422,11 +428,10 @@ void sqlite3DeleteFrom(
|
||||
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
||||
}
|
||||
|
||||
if( okOnePass ){
|
||||
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
||||
if( eOnePass ){
|
||||
/* 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
|
||||
** delete code.
|
||||
*/
|
||||
** delete code. */
|
||||
nKey = nPk; /* OP_Found will use an unpacked key */
|
||||
aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
|
||||
if( aToOpen==0 ){
|
||||
@ -438,27 +443,27 @@ void sqlite3DeleteFrom(
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
||||
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
||||
addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
|
||||
}else if( pPk ){
|
||||
/* Construct a composite key for the row to be deleted and remember it */
|
||||
iKey = ++pParse->nMem;
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
}else{
|
||||
/* Get the rowid of the row to be deleted and remember it in the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
if( pPk ){
|
||||
/* Add the PK key for this row to the temporary table */
|
||||
iKey = ++pParse->nMem;
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
}else{
|
||||
/* Add the rowid of the row to be deleted to the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
}
|
||||
}
|
||||
|
||||
/* End of the WHERE loop */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
if( okOnePass ){
|
||||
/* Bypass the delete logic below if the WHERE loop found zero rows */
|
||||
/* If this DELETE cannot use the ONEPASS strategy, this is the
|
||||
** end of the WHERE loop */
|
||||
if( eOnePass ){
|
||||
addrBypass = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeGoto(v, addrBypass);
|
||||
sqlite3VdbeJumpHere(v, addrDelete);
|
||||
}else{
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
/* Unless this is a view, open cursors for the table we are
|
||||
@ -467,20 +472,21 @@ void sqlite3DeleteFrom(
|
||||
** triggers.
|
||||
*/
|
||||
if( !isView ){
|
||||
int iAddrOnce = 0;
|
||||
if( eOnePass==2 ) iAddrOnce = sqlite3CodeOnce(pParse);
|
||||
testcase( IsVirtual(pTab) );
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
|
||||
&iDataCur, &iIdxCur);
|
||||
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
|
||||
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
|
||||
** where-clause loop above.
|
||||
*/
|
||||
if( okOnePass ){
|
||||
/* Just one row. Hence the top-of-loop is a no-op */
|
||||
if( eOnePass ){
|
||||
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
||||
assert( !IsVirtual(pTab) );
|
||||
if( aToOpen[iDataCur-iTabCur] ){
|
||||
assert( pPk!=0 || pTab->pSelect!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
||||
@ -508,13 +514,18 @@ void sqlite3DeleteFrom(
|
||||
#endif
|
||||
{
|
||||
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,
|
||||
iKey, nKey, count, OE_Default, okOnePass);
|
||||
iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
|
||||
}
|
||||
|
||||
/* End of the loop over all rowids/primary-keys. */
|
||||
if( okOnePass ){
|
||||
if( eOnePass ){
|
||||
sqlite3VdbeResolveLabel(v, addrBypass);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addrLoop);
|
||||
@ -586,6 +597,25 @@ delete_from_cleanup:
|
||||
** 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
|
||||
** 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(
|
||||
Parse *pParse, /* Parsing context */
|
||||
@ -597,7 +627,8 @@ void sqlite3GenerateRowDelete(
|
||||
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
||||
u8 count, /* If non-zero, increment the row change counter */
|
||||
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 */
|
||||
int iOld = 0; /* First register in OLD.* array */
|
||||
@ -614,7 +645,7 @@ void sqlite3GenerateRowDelete(
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel(v);
|
||||
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
if( !bNoSeek ){
|
||||
if( eMode==0 ){
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
||||
@ -674,11 +705,15 @@ void sqlite3GenerateRowDelete(
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
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));
|
||||
if( count ){
|
||||
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
|
||||
@ -721,7 +756,8 @@ void sqlite3GenerateRowIndexDelete(
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iDataCur, /* Cursor of table holding data. */
|
||||
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 r1 = -1; /* Register holding an index key */
|
||||
@ -737,11 +773,12 @@ void sqlite3GenerateRowIndexDelete(
|
||||
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
||||
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
||||
if( pIdx==pPk ) continue;
|
||||
if( iIdxCur+i==iIdxNoSeek ) continue;
|
||||
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
|
||||
pPrior = pIdx;
|
||||
}
|
||||
|
@ -1347,10 +1347,10 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regNewData, 1, 0, OE_Replace, 1);
|
||||
regNewData, 1, 0, OE_Replace, 1, -1);
|
||||
}else if( pTab->pIndex ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0, -1);
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
@ -1528,7 +1528,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regR, nPkField, 0, OE_Replace, pIdx==pPk);
|
||||
regR, nPkField, 0, OE_Replace, pIdx==pPk, -1);
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -2352,6 +2352,7 @@ struct SrcList {
|
||||
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
|
||||
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
|
||||
#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()
|
||||
*/
|
||||
@ -3440,8 +3441,9 @@ int sqlite3ExprIsInteger(Expr*, int*);
|
||||
int sqlite3ExprCanBeNull(const Expr*);
|
||||
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
||||
int sqlite3IsRowid(const char*);
|
||||
void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
|
||||
void sqlite3GenerateRowDelete(
|
||||
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);
|
||||
void sqlite3ResolvePartIdxLabel(Parse*,int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
||||
|
@ -587,7 +587,7 @@ void sqlite3Update(
|
||||
}
|
||||
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( hasFK || chngKey || pPk!=0 ){
|
||||
|
33
src/vdbe.c
33
src/vdbe.c
@ -3968,9 +3968,10 @@ case OP_Found: { /* jump, in3 */
|
||||
**
|
||||
** P1 is the index of a cursor open on an SQL table btree (with integer
|
||||
** keys). P3 is an integer rowid. If P1 does not contain a record with
|
||||
** rowid P3 then jump immediately to P2. If P1 does contain a record
|
||||
** with rowid P3 then leave the cursor pointing at that record and fall
|
||||
** through to the next instruction.
|
||||
** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an
|
||||
** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
|
||||
** leave the cursor pointing at that record and fall through to the next
|
||||
** instruction.
|
||||
**
|
||||
** The OP_NotFound opcode performs the same operation on index btrees
|
||||
** (with arbitrary multi-value keys).
|
||||
@ -4008,7 +4009,10 @@ case OP_NotExists: { /* jump, in3 */
|
||||
pC->deferredMoveto = 0;
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
pC->seekResult = res;
|
||||
if( res!=0 ) goto jump_to_p2;
|
||||
if( res!=0 ){
|
||||
if( pOp->p2==0 && rc==SQLITE_OK ) rc = SQLITE_CORRUPT_BKPT;
|
||||
goto jump_to_p2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4274,14 +4278,15 @@ case OP_InsertInt: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Delete P1 P2 * P4 *
|
||||
/* Opcode: Delete P1 P2 * P4 P5
|
||||
**
|
||||
** Delete the record at which the P1 cursor is currently pointing.
|
||||
**
|
||||
** The cursor will be left pointing at either the next or the previous
|
||||
** record in the table. If it is left pointing at the next record, then
|
||||
** the next Next instruction will be a no-op. Hence it is OK to delete
|
||||
** a record from within a Next loop.
|
||||
** If the P5 parameter is non-zero, the cursor will be left pointing at
|
||||
** either the next or the previous record in the table. If it is left
|
||||
** pointing at the next record, then the next Next instruction will be a
|
||||
** 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
|
||||
** incremented (otherwise not).
|
||||
@ -4303,18 +4308,22 @@ case OP_Delete: {
|
||||
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
|
||||
assert( pC->deferredMoveto==0 );
|
||||
|
||||
if( pOp->p5 && db->xUpdateCallback && pOp->p4.z && pC->isTable ){
|
||||
sqlite3BtreeKeySize(pC->pCursor, &pC->movetoTarget);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* 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
|
||||
** is being deleted */
|
||||
if( pOp->p4.z && pC->isTable ){
|
||||
if( pOp->p4.z && pC->isTable && pOp->p5==0 ){
|
||||
i64 iKey = 0;
|
||||
sqlite3BtreeKeySize(pC->pCursor, &iKey);
|
||||
assert( pC->movetoTarget==iKey );
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = sqlite3BtreeDelete(pC->pCursor);
|
||||
rc = sqlite3BtreeDelete(pC->pCursor, pOp->p5);
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
||||
/* Invoke the update-hook if required. */
|
||||
@ -4857,7 +4866,7 @@ case OP_IdxDelete: {
|
||||
#endif
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
|
||||
if( rc==SQLITE_OK && res==0 ){
|
||||
rc = sqlite3BtreeDelete(pCrsr);
|
||||
rc = sqlite3BtreeDelete(pCrsr, 0);
|
||||
}
|
||||
assert( pC->deferredMoveto==0 );
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
19
src/where.c
19
src/where.c
@ -3958,6 +3958,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3 *db; /* Database connection */
|
||||
int rc; /* Return code */
|
||||
|
||||
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
|
||||
(wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
));
|
||||
|
||||
/* Variable initialization */
|
||||
db = pParse->db;
|
||||
@ -4199,11 +4203,16 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** the statement to update or delete a single row.
|
||||
*/
|
||||
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
|
||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||
&& (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
|
||||
pWInfo->okOnePass = 1;
|
||||
if( HasRowid(pTabList->a[0].pTab) ){
|
||||
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
|
||||
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
|
||||
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) ){
|
||||
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1069,7 +1069,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
if( pWInfo->okOnePass ){
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
}
|
||||
}else if( iCur!=iIdxCur ){
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
|
@ -68,7 +68,6 @@ do_test delete-3.1.7 {
|
||||
} {1 2 4 16}
|
||||
integrity_check delete-3.2
|
||||
|
||||
|
||||
# Semantic errors in the WHERE clause
|
||||
#
|
||||
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 {
|
||||
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 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
do_execsql_test indexedby-7.3 {
|
||||
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 {
|
||||
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=?)}}
|
||||
|
Loading…
Reference in New Issue
Block a user