Changes to allow some multi-row UPDATE statements to avoid the two-pass
approach. FossilOrigin-Name: 46db23ccd116ce5b9d949f9293be8a2818411b46
This commit is contained in:
parent
f112f0b3de
commit
f91c1318f4
30
manifest
30
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sa\stest\scase\sfor\sticket\s[25e335f802dd].
|
||||
D 2017-01-10T17:37:49.188
|
||||
C Changes\sto\sallow\ssome\smulti-row\sUPDATE\sstatements\sto\savoid\sthe\stwo-pass\napproach.
|
||||
D 2017-01-10T20:04:38.186
|
||||
F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
|
||||
@ -331,8 +331,8 @@ F src/auth.c 930b376a9c56998557367e6f7f8aaeac82a2a792
|
||||
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
||||
F src/btree.c d2c100618784bd89c089fcef03ff6e789768ecae
|
||||
F src/btree.h 2349a588abcd7e0c04f984e15c5c777b61637583
|
||||
F src/btree.c 44e9612965f63bef288673b81faa43e765bcac5f
|
||||
F src/btree.h e6d352808956ec163a17f832193a3e198b3fb0ac
|
||||
F src/btreeInt.h 10c4b77c2fb399580babbcc7cf652ac10dba796e
|
||||
F src/build.c 9e799f1edd910dfa8a0bc29bd390d35d310596af
|
||||
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
|
||||
@ -350,7 +350,7 @@ F src/hash.c 63d0ee752a3b92d4695b2b1f5259c4621b2cfebd
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 7761fd63136771d411f096f4d7a1af9c5057ddd4
|
||||
F src/insert.c 05e47e2de7b712a3a4148cd469e5f60873f5ef13
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c 5d6642d141c07d366e43d359e94ec9de47add41d
|
||||
F src/main.c e207b81542d13b9f13d61e78ca441f9781f055b0
|
||||
@ -388,12 +388,12 @@ F src/printf.c ff10a9b9902cd2afe5f655f3013c6307d969b1fd
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c bb070cf5f23611c44ab7e4788803684e385fc3fb
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 533e55a4067278fef76eff951462383d4147880f
|
||||
F src/select.c 3856db523b942062bca8722ba03b61c324ff94d6
|
||||
F src/shell.c 6095531aa900decdaa765e0f3993fba7153c92c1
|
||||
F src/sqlite.h.in e71655293c9bde26939496f3aac9d1821d2c07a2
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
F src/sqliteInt.h 9fdfb8789b27a621f3401468bc1705c32308f877
|
||||
F src/sqliteInt.h bec6274d8991528bc12d9a34d01fe84bdf6d00d9
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
@ -451,11 +451,11 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 5c2f516876fc27fbd7753913f032f49eb89e83b5
|
||||
F src/treeview.c 4e44ade3bfe59d82005039f72e09333ce2b4162c
|
||||
F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
|
||||
F src/update.c 1da7c462110bffed442a42884cb0d528c1db46d8
|
||||
F src/update.c 77a02122e040b8c6e1f13db9ef203e2224dae8f8
|
||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c a88b0466fddf445ce752226d4698ca3faada620a
|
||||
F src/vacuum.c 33c174b28886b2faf26e503b5a49a1c01a9b1c16
|
||||
F src/vdbe.c 4c239b73d8df6ccd82842e2de0a882be46f6152d
|
||||
F src/vdbe.c c7add5978cb84ae3a7bcb16f8b56cb3bbdf04b7e
|
||||
F src/vdbe.h b0866e4191f096f1c987a84b042c3599bdf5423b
|
||||
F src/vdbeInt.h 281cb70332dc8b593b8c7afe776f3a2ba7d4255e
|
||||
F src/vdbeapi.c d6ebaa465f070eb1af8ba4e7b34583ece87bdd24
|
||||
@ -469,7 +469,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
|
||||
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
|
||||
F src/walker.c 91a6df7435827e41cff6bb7df50ea00934ee78b0
|
||||
F src/where.c 6bbf9284f4f15a6fa48663d033870cc0d7f5ee66
|
||||
F src/where.c bc71775e23d23334e8f449aa31012d692dc09cb2
|
||||
F src/whereInt.h 2bcc3d176e6091cb8f50a30b65c006e88a73614d
|
||||
F src/wherecode.c e04ac8f24c3ac8621df6c3be3ac8c7d4fa893745
|
||||
F src/whereexpr.c 35ad025389a632a3987a35617c878be3b3d70dc6
|
||||
@ -1351,6 +1351,7 @@ F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
|
||||
F test/update2.test e7f23b8ed101b7113c198bbd6f78f508718725c1
|
||||
F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568
|
||||
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
|
||||
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
|
||||
@ -1543,7 +1544,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P c92ecff2ec5f178433d21f25c653d0fdd9128d7c
|
||||
R e5ba5e796b740bb6ab62d42fde094373
|
||||
P e500c15a9f55aed1601f7c14169dd56fd76f1fdd
|
||||
R 210f82ca33b63d0d25de1e8cfd9403fd
|
||||
T *branch * onepass-update
|
||||
T *sym-onepass-update *
|
||||
T -sym-trunk *
|
||||
U dan
|
||||
Z ef9b8c72a2a40eee5e78f0752f5e6ce0
|
||||
Z 00c5ac8bc1c536bfb67f723788417749
|
||||
|
@ -1 +1 @@
|
||||
e500c15a9f55aed1601f7c14169dd56fd76f1fdd
|
||||
46db23ccd116ce5b9d949f9293be8a2818411b46
|
31
src/btree.c
31
src/btree.c
@ -7948,7 +7948,7 @@ static int balance(BtCursor *pCur){
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const BtreePayload *pX, /* Content of the row to be inserted */
|
||||
int appendBias, /* True if this is likely an append */
|
||||
int flags, /* True if this is likely an append */
|
||||
int seekResult /* Result of prior MovetoUnpacked() call */
|
||||
){
|
||||
int rc;
|
||||
@ -7961,6 +7961,8 @@ int sqlite3BtreeInsert(
|
||||
unsigned char *oldCell;
|
||||
unsigned char *newCell = 0;
|
||||
|
||||
assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags );
|
||||
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
assert( pCur->skipNext!=SQLITE_OK );
|
||||
return pCur->skipNext;
|
||||
@ -8001,6 +8003,11 @@ int sqlite3BtreeInsert(
|
||||
** cursors open on the row being replaced */
|
||||
invalidateIncrblobCursors(p, pX->nKey, 0);
|
||||
|
||||
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
||||
** to a row with the same key as the new entry being inserted. */
|
||||
assert( (flags & BTREE_SAVEPOSITION)==0 ||
|
||||
((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) );
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
@ -8010,10 +8017,10 @@ int sqlite3BtreeInsert(
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
loc = -1;
|
||||
}else if( loc==0 ){
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 ){
|
||||
}else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
||||
if( pX->nMem ){
|
||||
UnpackedRecord r;
|
||||
r.pKeyInfo = pCur->pKeyInfo;
|
||||
@ -8024,9 +8031,9 @@ int sqlite3BtreeInsert(
|
||||
r.r1 = 0;
|
||||
r.r2 = 0;
|
||||
r.eqSeen = 0;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
||||
}else{
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@ -8114,6 +8121,20 @@ int sqlite3BtreeInsert(
|
||||
** from trying to save the current position of the cursor. */
|
||||
pCur->apPage[pCur->iPage]->nOverflow = 0;
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){
|
||||
rc = moveToRoot(pCur);
|
||||
if( pCur->pKeyInfo && rc==SQLITE_OK ){
|
||||
assert( pCur->pKey==0 );
|
||||
pCur->pKey = sqlite3Malloc( pX->nKey );
|
||||
if( pCur->pKey==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memcpy(pCur->pKey, pX->pKey, pX->nKey);
|
||||
}
|
||||
}
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
pCur->nKey = pX->nKey;
|
||||
}
|
||||
}
|
||||
assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
|
||||
|
||||
|
@ -249,9 +249,10 @@ int sqlite3BtreeCursorHasMoved(BtCursor*);
|
||||
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
|
||||
/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */
|
||||
/* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
|
||||
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
|
||||
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
|
||||
#define BTREE_APPEND 0x08 /* Insert is likely an append */
|
||||
|
||||
/* An instance of the BtreePayload object describes the content of a single
|
||||
** entry in either an index or table btree.
|
||||
@ -282,7 +283,7 @@ struct BtreePayload {
|
||||
};
|
||||
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int bias, int seekResult);
|
||||
int flags, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
|
10
src/insert.c
10
src/insert.c
@ -1684,7 +1684,7 @@ void sqlite3CompleteInsertion(
|
||||
int iIdxCur, /* First index cursor */
|
||||
int regNewData, /* Range of content */
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int update_flags, /* True for UPDATE, False for INSERT */
|
||||
int appendBias, /* True if this is likely to be an append */
|
||||
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
|
||||
){
|
||||
@ -1696,6 +1696,11 @@ void sqlite3CompleteInsertion(
|
||||
int i; /* Loop counter */
|
||||
u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
|
||||
|
||||
assert( update_flags==0
|
||||
|| update_flags==OPFLAG_ISUPDATE
|
||||
|| update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION)
|
||||
);
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
@ -1714,6 +1719,7 @@ void sqlite3CompleteInsertion(
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
assert( pParse->nested==0 );
|
||||
pik_flags |= OPFLAG_NCHANGE;
|
||||
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
|
||||
}
|
||||
sqlite3VdbeChangeP5(v, pik_flags);
|
||||
}
|
||||
@ -1729,7 +1735,7 @@ void sqlite3CompleteInsertion(
|
||||
pik_flags = 0;
|
||||
}else{
|
||||
pik_flags = OPFLAG_NCHANGE;
|
||||
pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID);
|
||||
pik_flags |= (update_flags?update_flags:OPFLAG_LASTROWID);
|
||||
}
|
||||
if( appendBias ){
|
||||
pik_flags |= OPFLAG_APPEND;
|
||||
|
@ -5652,7 +5652,7 @@ int sqlite3Select(
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax,0,flag,0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax, 0,flag,0);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
|
@ -3041,7 +3041,7 @@ struct AuthContext {
|
||||
#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
|
||||
/* Also used in P2 (not P5) of OP_Delete */
|
||||
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
|
||||
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
|
||||
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
|
||||
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
|
||||
@ -3055,7 +3055,7 @@ struct AuthContext {
|
||||
#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
|
||||
#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
|
||||
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
|
||||
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
|
||||
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */
|
||||
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
|
||||
|
||||
/*
|
||||
|
129
src/update.c
129
src/update.c
@ -105,7 +105,7 @@ void sqlite3Update(
|
||||
int iDataCur; /* Cursor for the canonical data btree */
|
||||
int iIdxCur; /* Cursor for the first index */
|
||||
sqlite3 *db; /* The database structure */
|
||||
int *aRegIdx = 0; /* One register assigned to each index to be updated */
|
||||
int *aRegIdx = 0; /* First register in array assigned to each index */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
@ -117,10 +117,11 @@ void sqlite3Update(
|
||||
AuthContext sContext; /* The authorization context */
|
||||
NameContext sNC; /* The name-context to resolve expressions in */
|
||||
int iDb; /* Database containing the table being updated */
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int eOnePass; /* ONEPASS_XXX value from where.c */
|
||||
int hasFK; /* True if foreign key processing is required */
|
||||
int labelBreak; /* Jump here to break out of UPDATE loop */
|
||||
int labelContinue; /* Jump here to continue next step of UPDATE loop */
|
||||
int flags; /* Flags for sqlite3WhereBegin() */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True when updating a view (INSTEAD OF trigger) */
|
||||
@ -131,6 +132,9 @@ void sqlite3Update(
|
||||
int iEph = 0; /* Ephemeral table holding all primary key values */
|
||||
int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
int addrOpen; /* Address of OP_OpenEphemeral */
|
||||
int iPk; /* First of nPk cells holding PRIMARY KEY value */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
@ -349,51 +353,70 @@ void sqlite3Update(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
/* Initialize the count of updated rows */
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else{
|
||||
int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
int addrOpen; /* Address of the OpenEphemeral instruction */
|
||||
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
regKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
}
|
||||
|
||||
/* Begin the database scan. */
|
||||
flags = WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE;
|
||||
if( pParse->nested==0 && pTrigger==0 && hasFK==0 && chngKey==0 ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* A one-pass strategy that might update more than one row may not
|
||||
** be used if any column of the index used for the scan is being
|
||||
** updated. Otherwise, if there is an index on "b", statements like
|
||||
** the following could create an infinite loop:
|
||||
**
|
||||
** UPDATE t1 SET b=b+1 WHERE b>?
|
||||
**
|
||||
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
||||
** strategy that uses an index for which one or more columns are being
|
||||
** updated. */
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
int iCur = aiCurOnePass[1];
|
||||
if( iCur>=0 && aToOpen[iCur-iBaseCur] ) eOnePass = ONEPASS_OFF;
|
||||
}
|
||||
|
||||
if( HasRowid(pTab) ){
|
||||
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
||||
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
||||
** leave it in register regOldRowid. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
}else{
|
||||
/* Read the PK of the current row into an array of registers. In
|
||||
** ONEPASS_OFF mode, serialize the array into a record and store it in
|
||||
** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
|
||||
** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
|
||||
** is not required) and leave the PK fields in the array of registers. */
|
||||
for(i=0; i<nPk; i++){
|
||||
assert( pPk->aiColumn[i]>=0 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
|
||||
iPk+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
|
||||
}
|
||||
if( okOnePass ){
|
||||
if( eOnePass ){
|
||||
sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
nKey = nPk;
|
||||
regKey = iPk;
|
||||
@ -402,18 +425,15 @@ void sqlite3Update(
|
||||
sqlite3IndexAffinityStr(db, pPk), nPk);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
if( eOnePass!=ONEPASS_MULTI ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
if( !isView ){
|
||||
int iAddrOnce = 0;
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
** index could potentially invoke a REPLACE conflict resolution
|
||||
@ -430,22 +450,31 @@ void sqlite3Update(
|
||||
}
|
||||
}
|
||||
}
|
||||
if( okOnePass ){
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
||||
}
|
||||
|
||||
if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
||||
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
|
||||
0, 0);
|
||||
if( iAddrOnce ) sqlite3VdbeJumpHere(v, iAddrOnce);
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( okOnePass ){
|
||||
if( aToOpen[iDataCur-iBaseCur] && !isView ){
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
||||
assert( pPk );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
labelContinue = labelBreak;
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
labelContinue = labelBreak;
|
||||
}else{
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||
VdbeCoverageIf(v, pPk==0);
|
||||
VdbeCoverageIf(v, pPk!=0);
|
||||
@ -606,14 +635,14 @@ void sqlite3Update(
|
||||
assert( regNew==regNewRowid+1 );
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
sqlite3VdbeAddOp3(v, OP_Delete, iDataCur,
|
||||
OPFLAG_ISUPDATE | ((hasFK || chngKey || pPk!=0) ? 0 : OPFLAG_ISNOOP),
|
||||
OPFLAG_ISUPDATE | ((hasFK || chngKey) ? 0 : OPFLAG_ISNOOP),
|
||||
regNewRowid
|
||||
);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
}
|
||||
#else
|
||||
if( hasFK || chngKey || pPk!=0 ){
|
||||
if( hasFK || chngKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
|
||||
}
|
||||
#endif
|
||||
@ -626,8 +655,11 @@ void sqlite3Update(
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regNewRowid, aRegIdx, 1, 0, 0);
|
||||
sqlite3CompleteInsertion(
|
||||
pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx,
|
||||
OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0),
|
||||
0, 0
|
||||
);
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
@ -649,8 +681,11 @@ void sqlite3Update(
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
if( okOnePass ){
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
/* Nothing to do at end-of-loop for a single-pass */
|
||||
}else if( eOnePass==ONEPASS_MULTI ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
||||
|
@ -4421,7 +4421,7 @@ case OP_InsertInt: {
|
||||
}
|
||||
x.pKey = 0;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult
|
||||
);
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
@ -5086,7 +5086,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
x.aMem = aMem + pOp->p3;
|
||||
x.nMem = (u16)pOp->p4.i;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & OPFLAG_APPEND)!=0,
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)),
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
|
@ -4949,7 +4949,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pOp->p2 = x;
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
}
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 );
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0
|
||||
|| pWInfo->eOnePass );
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
pOp->opcode = OP_IdxRowid;
|
||||
|
84
test/update2.test
Normal file
84
test/update2.test
Normal file
@ -0,0 +1,84 @@
|
||||
# 2017 January 9
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix update2
|
||||
|
||||
db func repeat [list string repeat]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 1.1.* A one-pass UPDATE that does balance() operations on the IPK index
|
||||
# that it is scanning.
|
||||
#
|
||||
# 1.2.* Same again, but with a WITHOUT ROWID table.
|
||||
#
|
||||
set nrow [expr 10]
|
||||
do_execsql_test 1.1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow )
|
||||
INSERT INTO t1(b) SELECT char((i % 26) + 65) FROM s;
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
UPDATE t1 SET b = repeat(b, 100)
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.2 {
|
||||
SELECT * FROM t1;
|
||||
} [db eval { SELECT a, repeat(b, 100) FROM t2 }]
|
||||
|
||||
do_execsql_test 1.2.0 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT ROWID;
|
||||
WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow )
|
||||
INSERT INTO t1(a, b) SELECT i+1, char((i % 26) + 65) FROM s;
|
||||
}
|
||||
|
||||
#explain_i { UPDATE t1 SET b = repeat(b, 100) }
|
||||
do_execsql_test 1.2.1 {
|
||||
UPDATE t1 SET b = repeat(b, 100)
|
||||
}
|
||||
|
||||
do_execsql_test 1.2.2 {
|
||||
SELECT * FROM t1;
|
||||
} [db eval { SELECT a, repeat(b, 100) FROM t2 }]
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# A one-pass UPDATE that does balance() operations on the IPK index
|
||||
# that it is scanning.
|
||||
#
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t3(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX t3i ON t3(b);
|
||||
} {}
|
||||
do_execsql_test 2.2 { UPDATE t3 SET c=1 WHERE b=? } {}
|
||||
do_execsql_test 2.3 { UPDATE t3 SET c=1 WHERE rowid=? } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t4(a PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
CREATE INDEX t4c ON t4(c);
|
||||
INSERT INTO t4 VALUES(1, 2, 3);
|
||||
INSERT INTO t4 VALUES(2, 3, 4);
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
UPDATE t4 SET c=c+2 WHERE c>2;
|
||||
SELECT a, c FROM t4 ORDER BY a;
|
||||
} {1 5 2 6}
|
||||
|
||||
finish_test
|
Loading…
Reference in New Issue
Block a user