mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Back out changes allowing writes to tables that have open cursors. (CVS 2133)
FossilOrigin-Name: 91acd87e52509a8f78894d0f4b625b54376cac21
This commit is contained in:
22
manifest
22
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Modify\stest\ssuite\sto\swork\swhen\sSQLITE_OMIT_VIEW\sis\sdefined.\s(CVS\s2132)
|
C Back\sout\schanges\sallowing\swrites\sto\stables\sthat\shave\sopen\scursors.\s(CVS\s2133)
|
||||||
D 2004-11-22T08:43:32
|
D 2004-11-22T10:02:10
|
||||||
F Makefile.in 8291610f5839939a5fbff4dbbf85adb0fe1ac37f
|
F Makefile.in 8291610f5839939a5fbff4dbbf85adb0fe1ac37f
|
||||||
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
||||||
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
||||||
@@ -29,11 +29,11 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
|
|||||||
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
||||||
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
|
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
|
||||||
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
|
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
|
||||||
F src/btree.c 614ac85f810a636d375da58d0e069896eb467b1a
|
F src/btree.c edcd4abafe0db11081e4e2db854b17649bec27d9
|
||||||
F src/btree.h 861e40b759a195ba63819740e484390012cf81ab
|
F src/btree.h 861e40b759a195ba63819740e484390012cf81ab
|
||||||
F src/build.c c7dd57fdbb330d65df241277291cad8e58687a46
|
F src/build.c c7dd57fdbb330d65df241277291cad8e58687a46
|
||||||
F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f
|
F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f
|
||||||
F src/delete.c be9d039b819f4a5d0fdfaeceace139ba189ef819
|
F src/delete.c cf185995e20a61c0fecc2a9a9a3b19bd18bd05b3
|
||||||
F src/expr.c 511c27a8858ca12614f495c9c90f5d12db11e6c2
|
F src/expr.c 511c27a8858ca12614f495c9c90f5d12db11e6c2
|
||||||
F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb
|
F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb
|
||||||
F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
|
F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
|
||||||
@@ -58,10 +58,10 @@ F src/parse.y 0a4bdfd7b65d9761b41a862d09a17c90c1f526f7
|
|||||||
F src/pragma.c 0b43b8cac4870bfa041bf2ca29d7ce47b76362d6
|
F src/pragma.c 0b43b8cac4870bfa041bf2ca29d7ce47b76362d6
|
||||||
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
|
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
|
||||||
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
||||||
F src/select.c cf4b7952d6d214931c52636ee726f19ee2a275c5
|
F src/select.c 7b17db766e669fc85837af50cc110d0988d31ee9
|
||||||
F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51
|
F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51
|
||||||
F src/sqlite.h.in 6d0e82c24ef3f84a10b468119f3943a5dfc806c7
|
F src/sqlite.h.in 6d0e82c24ef3f84a10b468119f3943a5dfc806c7
|
||||||
F src/sqliteInt.h 8f3b4d0e90c294e0e22b20a9f7f32f2523b8894c
|
F src/sqliteInt.h d16d54eeefb24b4b1d0e9fa80593bf110a293aab
|
||||||
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
||||||
F src/tclsqlite.c 7f1a1a678140e6901c8954590ca2aabe50b48f71
|
F src/tclsqlite.c 7f1a1a678140e6901c8954590ca2aabe50b48f71
|
||||||
F src/test1.c 21b1cc9358678da579d7aad8f16a40735a837078
|
F src/test1.c 21b1cc9358678da579d7aad8f16a40735a837078
|
||||||
@@ -71,7 +71,7 @@ F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df
|
|||||||
F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5
|
F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5
|
||||||
F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a
|
F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a
|
||||||
F src/trigger.c 0c91b56182560263733e4b035acdb939bd1cf0e2
|
F src/trigger.c 0c91b56182560263733e4b035acdb939bd1cf0e2
|
||||||
F src/update.c 395a2b270dfcbc96c20e40c9cb42b0533768ce30
|
F src/update.c 24a605a4250fe4a0b173bedd7132d1892ad27090
|
||||||
F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a
|
F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a
|
||||||
F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e
|
F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e
|
||||||
F src/vacuum.c d061dd908a9e809c54e40e24a551b1d64abd3d16
|
F src/vacuum.c d061dd908a9e809c54e40e24a551b1d64abd3d16
|
||||||
@@ -81,7 +81,7 @@ F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b
|
|||||||
F src/vdbeapi.c 74be7f96c0a1ac275661f8b32276ac521d9ce37c
|
F src/vdbeapi.c 74be7f96c0a1ac275661f8b32276ac521d9ce37c
|
||||||
F src/vdbeaux.c dc06bbb8511d07f8d45ed2ea760f35f0736a690c
|
F src/vdbeaux.c dc06bbb8511d07f8d45ed2ea760f35f0736a690c
|
||||||
F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0
|
F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0
|
||||||
F src/where.c 4d28167e450255372b45abf1bc8cd5f0e9264d7b
|
F src/where.c f8a9e0bca6cb0a6fc4c189ed9fa771e75ad68bc8
|
||||||
F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c
|
F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c
|
||||||
F test/alter.test 2b4478c4906e4d1504b1a121b4ffbc8d11043b53
|
F test/alter.test 2b4478c4906e4d1504b1a121b4ffbc8d11043b53
|
||||||
F test/attach.test bb0ff048b0a65ca2bd5f186eee05d2ec084f3972
|
F test/attach.test bb0ff048b0a65ca2bd5f186eee05d2ec084f3972
|
||||||
@@ -261,7 +261,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
|
|||||||
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
|
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
|
||||||
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
||||||
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
|
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
|
||||||
P 9df837c03939cdcb31856ac17b2425a6dd92d7b2
|
P 711e8d7695dfc74b3f1ee00591dcdda2cd7fc7d5
|
||||||
R 58a8d6a23d66b3c503089009ebe0b762
|
R 435be4c4df18ad02a5220daa06c920af
|
||||||
U danielk1977
|
U danielk1977
|
||||||
Z 2746b3ff0377ed15d41d60a317d903c0
|
Z 690d6154f35eb3573c00acd3211a6213
|
||||||
|
@@ -1 +1 @@
|
|||||||
711e8d7695dfc74b3f1ee00591dcdda2cd7fc7d5
|
91acd87e52509a8f78894d0f4b625b54376cac21
|
370
src/btree.c
370
src/btree.c
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.222 2004/11/22 05:26:27 danielk1977 Exp $
|
** $Id: btree.c,v 1.223 2004/11/22 10:02:10 danielk1977 Exp $
|
||||||
**
|
**
|
||||||
** This file implements a external (disk-based) database using BTrees.
|
** This file implements a external (disk-based) database using BTrees.
|
||||||
** For a detailed discussion of BTrees, refer to
|
** For a detailed discussion of BTrees, refer to
|
||||||
@@ -344,16 +344,6 @@ struct CellInfo {
|
|||||||
** A cursor is a pointer to a particular entry in the BTree.
|
** A cursor is a pointer to a particular entry in the BTree.
|
||||||
** The entry is identified by its MemPage and the index in
|
** The entry is identified by its MemPage and the index in
|
||||||
** MemPage.aCell[] of the entry.
|
** MemPage.aCell[] of the entry.
|
||||||
**
|
|
||||||
** Normally, the BtCursor.delShift variable is 0. If non-zero, this
|
|
||||||
** indicates that the entry to which the cursor logically points
|
|
||||||
** was deleted (by a BtreeDelete() call). If this is the case, the
|
|
||||||
** BtreeKeySize() and BtreeDataSize() calls both return 0.
|
|
||||||
|
|
||||||
** If BtCursor.delShift is +1, then do not move the cursor for a
|
|
||||||
** BtreeNext() operation (it was already advanced when the entry the
|
|
||||||
** cursor logically points to was deleted). If BtCursor.delShift is
|
|
||||||
** -1, then ignore the next BtreePrevious() call.
|
|
||||||
*/
|
*/
|
||||||
struct BtCursor {
|
struct BtCursor {
|
||||||
Btree *pBt; /* The Btree to which this cursor belongs */
|
Btree *pBt; /* The Btree to which this cursor belongs */
|
||||||
@@ -367,7 +357,6 @@ struct BtCursor {
|
|||||||
u8 wrFlag; /* True if writable */
|
u8 wrFlag; /* True if writable */
|
||||||
u8 isValid; /* TRUE if points to a valid entry */
|
u8 isValid; /* TRUE if points to a valid entry */
|
||||||
u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */
|
u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */
|
||||||
int delShift; /* See above. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2125,7 +2114,6 @@ int sqlite3BtreeCursor(
|
|||||||
pBt->pCursor = pCur;
|
pBt->pCursor = pCur;
|
||||||
pCur->isValid = 0;
|
pCur->isValid = 0;
|
||||||
pCur->status = SQLITE_OK;
|
pCur->status = SQLITE_OK;
|
||||||
pCur->delShift = 0;
|
|
||||||
*ppCur = pCur;
|
*ppCur = pCur;
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
|
|
||||||
@@ -2224,7 +2212,7 @@ static void getCellInfo(BtCursor *pCur){
|
|||||||
** itself, not the number of bytes in the key.
|
** itself, not the number of bytes in the key.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
||||||
if( !pCur->isValid || pCur->delShift ){
|
if( !pCur->isValid ){
|
||||||
*pSize = 0;
|
*pSize = 0;
|
||||||
}else{
|
}else{
|
||||||
getCellInfo(pCur);
|
getCellInfo(pCur);
|
||||||
@@ -2241,7 +2229,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
|||||||
** the database is empty) then *pSize is set to 0.
|
** the database is empty) then *pSize is set to 0.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
|
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
|
||||||
if( !pCur->isValid || pCur->delShift ){
|
if( !pCur->isValid ){
|
||||||
/* Not pointing at a valid entry - set *pSize to 0. */
|
/* Not pointing at a valid entry - set *pSize to 0. */
|
||||||
*pSize = 0;
|
*pSize = 0;
|
||||||
}else{
|
}else{
|
||||||
@@ -2352,7 +2340,7 @@ static int getPayload(
|
|||||||
** the available payload.
|
** the available payload.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||||
if( !pCur->isValid || pCur->delShift ){
|
if( !pCur->isValid ){
|
||||||
return pCur->status;
|
return pCur->status;
|
||||||
}
|
}
|
||||||
assert( pCur->pPage!=0 );
|
assert( pCur->pPage!=0 );
|
||||||
@@ -2371,7 +2359,7 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
|||||||
** the available payload.
|
** the available payload.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||||
if( !pCur->isValid || pCur->delShift ){
|
if( !pCur->isValid ){
|
||||||
return pCur->status ? pCur->status : SQLITE_INTERNAL;
|
return pCur->status ? pCur->status : SQLITE_INTERNAL;
|
||||||
}
|
}
|
||||||
assert( pCur->pPage!=0 );
|
assert( pCur->pPage!=0 );
|
||||||
@@ -2559,7 +2547,6 @@ static int moveToRoot(BtCursor *pCur){
|
|||||||
rc = moveToChild(pCur, subpage);
|
rc = moveToChild(pCur, subpage);
|
||||||
}
|
}
|
||||||
pCur->isValid = pCur->pPage->nCell>0;
|
pCur->isValid = pCur->pPage->nCell>0;
|
||||||
pCur->delShift = 0;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2797,15 +2784,6 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
|||||||
assert( pPage->isInit );
|
assert( pPage->isInit );
|
||||||
assert( pCur->idx<pPage->nCell );
|
assert( pCur->idx<pPage->nCell );
|
||||||
|
|
||||||
/* If BtCursor.delShift is 1, the cursor has already been advanced. */
|
|
||||||
if( pCur->delShift==1 ){
|
|
||||||
*pRes = 0;
|
|
||||||
pCur->delShift = 0;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}else{
|
|
||||||
pCur->delShift = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pCur->idx++;
|
pCur->idx++;
|
||||||
pCur->info.nSize = 0;
|
pCur->info.nSize = 0;
|
||||||
if( pCur->idx>=pPage->nCell ){
|
if( pCur->idx>=pPage->nCell ){
|
||||||
@@ -2856,15 +2834,6 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If BtCursor.delShift is -1, the cursor has already been advanced. */
|
|
||||||
if( pCur->delShift==-1 ){
|
|
||||||
*pRes = 0;
|
|
||||||
pCur->delShift = 0;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}else{
|
|
||||||
pCur->delShift = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pPage = pCur->pPage;
|
pPage = pCur->pPage;
|
||||||
assert( pPage->isInit );
|
assert( pPage->isInit );
|
||||||
assert( pCur->idx>=0 );
|
assert( pCur->idx>=0 );
|
||||||
@@ -3656,7 +3625,6 @@ static int balance_nonroot(MemPage *pPage){
|
|||||||
int *szCell; /* Local size of all cells in apCell[] */
|
int *szCell; /* Local size of all cells in apCell[] */
|
||||||
u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
|
u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
|
||||||
u8 *aSpace; /* Space to hold copies of dividers cells */
|
u8 *aSpace; /* Space to hold copies of dividers cells */
|
||||||
BtCursor *pCur;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Find the parent page.
|
** Find the parent page.
|
||||||
@@ -3905,6 +3873,16 @@ static int balance_nonroot(MemPage *pPage){
|
|||||||
zeroPage(pNew, pageFlags);
|
zeroPage(pNew, pageFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free any old pages that were not reused as new pages.
|
||||||
|
*/
|
||||||
|
while( i<nOld ){
|
||||||
|
rc = freePage(apOld[i]);
|
||||||
|
if( rc ) goto balance_cleanup;
|
||||||
|
releasePage(apOld[i]);
|
||||||
|
apOld[i] = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Put the new pages in accending order. This helps to
|
** Put the new pages in accending order. This helps to
|
||||||
** keep entries in the disk file in order so that a scan
|
** keep entries in the disk file in order so that a scan
|
||||||
@@ -3949,95 +3927,6 @@ static int balance_nonroot(MemPage *pPage){
|
|||||||
nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0,
|
nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0,
|
||||||
nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0));
|
nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0));
|
||||||
|
|
||||||
/* If there are other cursors that refer to one of the pages involved
|
|
||||||
** in the balancing, then adjust these cursors so that they still
|
|
||||||
** point to the same cells.
|
|
||||||
*/
|
|
||||||
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
|
|
||||||
int nCellCnt = 0;
|
|
||||||
int iCell = -1;
|
|
||||||
Pgno pgno = pCur->pPage->pgno;
|
|
||||||
|
|
||||||
/* If the cursor is not valid, do not do anything with it. */
|
|
||||||
if( !pCur->isValid ) continue;
|
|
||||||
|
|
||||||
/* If the cursor pointed to one of the cells moved around during the
|
|
||||||
** balancing, then set variable iCell to the index of the cell in apCell.
|
|
||||||
** This is used by the block below to figure out where the cell was
|
|
||||||
** moved to, and adjust the cursor appropriately.
|
|
||||||
**
|
|
||||||
** If the cursor points to the parent page, but the cell was not involved
|
|
||||||
** in the balance, then declare the cache of the cell-parse invalid, as a
|
|
||||||
** defragmentation may of occured during the balance. Also, if the index
|
|
||||||
** of the cell is greater than that of the divider cells, then it may
|
|
||||||
** need to be adjusted (in case there are now more or less divider cells
|
|
||||||
** than there were before the balancing).
|
|
||||||
*/
|
|
||||||
for(i=0; iCell<0 && i<nOld; i++){
|
|
||||||
if( pgno==apCopy[i]->pgno ){
|
|
||||||
iCell = nCellCnt + pCur->idx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1);
|
|
||||||
}
|
|
||||||
if( pgno==pParent->pgno ){
|
|
||||||
assert( !leafData );
|
|
||||||
assert( iCell==-1 );
|
|
||||||
if( pCur->idx>=nxDiv && pCur->idx<(nxDiv+nOld-1) ){
|
|
||||||
for(i=0; i<=(pCur->idx-nxDiv); i++){
|
|
||||||
iCell += (apCopy[i]->nCell + apCopy[i]->nOverflow + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( pCur->idx>=(nxDiv+nOld-1) ){
|
|
||||||
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur, pgno, pCur->idx, pgno, pCur->idx+(nNew-nOld)));
|
|
||||||
pCur->idx += (nNew-nOld);
|
|
||||||
}
|
|
||||||
pCur->info.nSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If iCell is greater than or equal to zero, then pCur points at a
|
|
||||||
** cell that was moved around during the balance. Figure out where
|
|
||||||
** the cell was moved to and adjust pCur to match.
|
|
||||||
*/
|
|
||||||
if( iCell>=0 ){
|
|
||||||
int idxNew;
|
|
||||||
Pgno pgnoNew;
|
|
||||||
int x = 0;
|
|
||||||
|
|
||||||
assert( iCell<nCell );
|
|
||||||
while( cntNew[x]<=iCell ) x++;
|
|
||||||
if( x>0 && !leafData && cntNew[x-1]==iCell ){
|
|
||||||
/* The cell that pCur points to is a divider cell in pParent. */
|
|
||||||
pgnoNew = pParent->pgno;
|
|
||||||
idxNew = nxDiv + x-1;
|
|
||||||
}else{
|
|
||||||
/* The cell that pCur points to is on page apNew[x]. */
|
|
||||||
idxNew = iCell-(x>0?cntNew[x-1]:0)-((leafData||x==0)?0:1);
|
|
||||||
pgnoNew = apNew[x]->pgno;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur, pgno, pCur->idx, pgnoNew, idxNew));
|
|
||||||
|
|
||||||
pCur->idx = idxNew;
|
|
||||||
releasePage(pCur->pPage);
|
|
||||||
rc = getPage(pBt, pgnoNew, &pCur->pPage);
|
|
||||||
assert( rc==SQLITE_OK );
|
|
||||||
assert( pCur->pPage->isInit );
|
|
||||||
pCur->info.nSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free any old pages that were not reused as new pages.
|
|
||||||
*/
|
|
||||||
for(i=nNew; i<nOld; i++){
|
|
||||||
rc = freePage(apOld[i]);
|
|
||||||
if( rc ) goto balance_cleanup;
|
|
||||||
releasePage(apOld[i]);
|
|
||||||
apOld[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Evenly distribute the data in apCell[] across the new pages.
|
** Evenly distribute the data in apCell[] across the new pages.
|
||||||
** Insert divider cells into pParent as necessary.
|
** Insert divider cells into pParent as necessary.
|
||||||
@@ -4181,7 +4070,6 @@ static int balance_shallower(MemPage *pPage){
|
|||||||
/* The child information will fit on the root page, so do the
|
/* The child information will fit on the root page, so do the
|
||||||
** copy */
|
** copy */
|
||||||
int i;
|
int i;
|
||||||
BtCursor *pCur;
|
|
||||||
zeroPage(pPage, pChild->aData[0]);
|
zeroPage(pPage, pChild->aData[0]);
|
||||||
for(i=0; i<pChild->nCell; i++){
|
for(i=0; i<pChild->nCell; i++){
|
||||||
apCell[i] = findCell(pChild,i);
|
apCell[i] = findCell(pChild,i);
|
||||||
@@ -4190,26 +4078,12 @@ static int balance_shallower(MemPage *pPage){
|
|||||||
assemblePage(pPage, pChild->nCell, apCell, szCell);
|
assemblePage(pPage, pChild->nCell, apCell, szCell);
|
||||||
freePage(pChild);
|
freePage(pChild);
|
||||||
TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno));
|
TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno));
|
||||||
/* If there were cursors pointing at this page, point them at the
|
|
||||||
** new page instead. Decrement the reference count for the old
|
|
||||||
** page and increment it for the new one.
|
|
||||||
*/
|
|
||||||
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
|
|
||||||
if( pCur->pPage==pChild ){
|
|
||||||
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur, pPage->pgno, pCur->idx, pPage->pgno, pCur->idx));
|
|
||||||
releasePage(pCur->pPage);
|
|
||||||
rc = getPage(pBt, 1, &pCur->pPage);
|
|
||||||
assert( rc==SQLITE_OK );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
/* The child has more information that will fit on the root.
|
/* The child has more information that will fit on the root.
|
||||||
** The tree is already balanced. Do nothing. */
|
** The tree is already balanced. Do nothing. */
|
||||||
TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
|
TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
BtCursor *pCur;
|
|
||||||
memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize);
|
memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize);
|
||||||
pPage->isInit = 0;
|
pPage->isInit = 0;
|
||||||
pPage->pParent = 0;
|
pPage->pParent = 0;
|
||||||
@@ -4218,15 +4092,6 @@ static int balance_shallower(MemPage *pPage){
|
|||||||
freePage(pChild);
|
freePage(pChild);
|
||||||
TRACE(("BALANCE: transfer child %d into root %d\n",
|
TRACE(("BALANCE: transfer child %d into root %d\n",
|
||||||
pChild->pgno, pPage->pgno));
|
pChild->pgno, pPage->pgno));
|
||||||
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
|
|
||||||
if( pCur->pPage==pChild ){
|
|
||||||
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur, pChild->pgno, pCur->idx, pPage->pgno, pCur->idx));
|
|
||||||
releasePage(pCur->pPage);
|
|
||||||
rc = getPage(pBt, pPage->pgno, &pCur->pPage);
|
|
||||||
assert( rc==SQLITE_OK );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rc = reparentChildPages(pPage);
|
rc = reparentChildPages(pPage);
|
||||||
if( rc!=SQLITE_OK ) goto end_shallow_balance;
|
if( rc!=SQLITE_OK ) goto end_shallow_balance;
|
||||||
@@ -4257,7 +4122,6 @@ static int balance_deeper(MemPage *pPage){
|
|||||||
u8 *cdata; /* Content of the child page */
|
u8 *cdata; /* Content of the child page */
|
||||||
int hdr; /* Offset to page header in parent */
|
int hdr; /* Offset to page header in parent */
|
||||||
int brk; /* Offset to content of first cell in parent */
|
int brk; /* Offset to content of first cell in parent */
|
||||||
BtCursor *pCur;
|
|
||||||
|
|
||||||
assert( pPage->pParent==0 );
|
assert( pPage->pParent==0 );
|
||||||
assert( pPage->nOverflow>0 );
|
assert( pPage->nOverflow>0 );
|
||||||
@@ -4284,21 +4148,6 @@ static int balance_deeper(MemPage *pPage){
|
|||||||
zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
|
zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
|
||||||
put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
|
put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
|
||||||
TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
|
TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
|
||||||
|
|
||||||
/* If there were cursors pointing at this page, point them at the new
|
|
||||||
** page instead. Decrement the reference count for the old page and
|
|
||||||
** increment it for the new one.
|
|
||||||
*/
|
|
||||||
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
|
|
||||||
if( pCur->pPage==pPage ){
|
|
||||||
TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur, pPage->pgno, pCur->idx, pChild->pgno, pCur->idx));
|
|
||||||
releasePage(pCur->pPage);
|
|
||||||
rc = getPage(pBt, pChild->pgno, &pCur->pPage);
|
|
||||||
assert( rc==SQLITE_OK );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = balance_nonroot(pChild);
|
rc = balance_nonroot(pChild);
|
||||||
releasePage(pChild);
|
releasePage(pChild);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -4331,8 +4180,24 @@ static int balance(MemPage *pPage){
|
|||||||
** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
|
** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
|
||||||
** cursors that point to pgnoRoot were opened with wrFlag==1
|
** cursors that point to pgnoRoot were opened with wrFlag==1
|
||||||
** then this routine returns SQLITE_OK.
|
** then this routine returns SQLITE_OK.
|
||||||
|
**
|
||||||
|
** In addition to checking for read-locks (where a read-lock
|
||||||
|
** means a cursor opened with wrFlag==0) this routine also moves
|
||||||
|
** all cursors other than pExclude so that they are pointing to the
|
||||||
|
** first Cell on root page. This is necessary because an insert
|
||||||
|
** or delete might change the number of cells on a page or delete
|
||||||
|
** a page entirely and we do not want to leave any cursors
|
||||||
|
** pointing to non-existant pages or cells.
|
||||||
*/
|
*/
|
||||||
static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){
|
static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){
|
||||||
|
BtCursor *p;
|
||||||
|
for(p=pBt->pCursor; p; p=p->pNext){
|
||||||
|
if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
|
||||||
|
if( p->wrFlag==0 ) return SQLITE_LOCKED;
|
||||||
|
if( p->pPage->pgno!=p->pgnoRoot ){
|
||||||
|
moveToRoot(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4357,7 +4222,6 @@ int sqlite3BtreeInsert(
|
|||||||
Btree *pBt = pCur->pBt;
|
Btree *pBt = pCur->pBt;
|
||||||
unsigned char *oldCell;
|
unsigned char *oldCell;
|
||||||
unsigned char *newCell = 0;
|
unsigned char *newCell = 0;
|
||||||
BtCursor *pCur2;
|
|
||||||
|
|
||||||
if( pCur->status ){
|
if( pCur->status ){
|
||||||
return pCur->status; /* A rollback destroyed this cursor */
|
return pCur->status; /* A rollback destroyed this cursor */
|
||||||
@@ -4409,30 +4273,13 @@ int sqlite3BtreeInsert(
|
|||||||
assert( pPage->leaf );
|
assert( pPage->leaf );
|
||||||
}
|
}
|
||||||
rc = insertCell(pPage, pCur->idx, newCell, szNew, 0);
|
rc = insertCell(pPage, pCur->idx, newCell, szNew, 0);
|
||||||
pCur->isValid = 1;
|
|
||||||
|
|
||||||
/* If there are other cursors pointing at this page with a BtCursor.idx
|
|
||||||
** field greater than or equal to 'i', then the cell they refer to
|
|
||||||
** has been modified or moved within the page. Fix the cursor.
|
|
||||||
*/
|
|
||||||
for(pCur2=pPage->pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
|
|
||||||
if( pCur2->pPage==pPage ){
|
|
||||||
if( pCur2->idx>=pCur->idx && pCur!=pCur2 && loc!=0 ){
|
|
||||||
/* The cell pointed to by pCur2 was shifted one to the right on it's
|
|
||||||
** page by this Insert().
|
|
||||||
*/
|
|
||||||
TRACE(("INSERT: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx+1));
|
|
||||||
pCur2->idx++;
|
|
||||||
}
|
|
||||||
pCur2->info.nSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( rc!=SQLITE_OK ) goto end_insert;
|
if( rc!=SQLITE_OK ) goto end_insert;
|
||||||
rc = balance(pPage);
|
rc = balance(pPage);
|
||||||
/* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
|
/* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
|
||||||
/* fflush(stdout); */
|
/* fflush(stdout); */
|
||||||
|
if( rc==SQLITE_OK ){
|
||||||
|
moveToRoot(pCur);
|
||||||
|
}
|
||||||
end_insert:
|
end_insert:
|
||||||
sqliteFree(newCell);
|
sqliteFree(newCell);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -4448,8 +4295,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
int rc;
|
int rc;
|
||||||
Pgno pgnoChild = 0;
|
Pgno pgnoChild = 0;
|
||||||
Btree *pBt = pCur->pBt;
|
Btree *pBt = pCur->pBt;
|
||||||
int idx; /* Index of the cell to delete */
|
|
||||||
BtCursor *pCur2; /* Iterator variable for the pBt.pCursor link-list */
|
|
||||||
|
|
||||||
assert( pPage->isInit );
|
assert( pPage->isInit );
|
||||||
if( pCur->status ){
|
if( pCur->status ){
|
||||||
@@ -4472,50 +4317,11 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
rc = sqlite3pager_write(pPage->aData);
|
rc = sqlite3pager_write(pPage->aData);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
|
|
||||||
/* Set index to the index in pPage that contains the cell to delete. Also
|
|
||||||
** increment the reference count for pPage. This allows us to move the
|
|
||||||
** cursor pCur before the delete takes place.
|
|
||||||
*/
|
|
||||||
idx = pCur->idx;
|
|
||||||
rc = getPage(pBt, pPage->pgno, &pPage);
|
|
||||||
if( rc ) return rc;
|
|
||||||
assert( pPage==pCur->pPage );
|
|
||||||
|
|
||||||
/* If there are any cursors that point to the cell being deleted,
|
|
||||||
** move them to the next or previous entry in the table. It is preferable
|
|
||||||
** to move the cursor to the 'next' location, rather than the 'previous'
|
|
||||||
** one, as most table scans are done in the forward direction (also, code
|
|
||||||
** below depends on this). If neither entry exists, declare the cursor
|
|
||||||
** invalid.
|
|
||||||
*/
|
|
||||||
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
|
|
||||||
if( pCur2->pPage==pPage && pCur2->idx==idx && pCur2->isValid ){
|
|
||||||
int res;
|
|
||||||
pCur2->delShift = 0;
|
|
||||||
rc = sqlite3BtreeNext(pCur2, &res);
|
|
||||||
if( rc ) goto delete_out;
|
|
||||||
if( res ){
|
|
||||||
/* If the next tree entry cannot be found, then the cursor must
|
|
||||||
** already point to the last table entry. So point it to the
|
|
||||||
** second last by calling BtreeLast(), BtreePrevious().
|
|
||||||
*/
|
|
||||||
rc = sqlite3BtreeLast(pCur2, &res);
|
|
||||||
if( rc ) goto delete_out;
|
|
||||||
assert( res==0 );
|
|
||||||
rc = sqlite3BtreePrevious(pCur2, &res);
|
|
||||||
if( rc ) goto delete_out;
|
|
||||||
pCur2->delShift = -1;
|
|
||||||
}else{
|
|
||||||
pCur2->delShift = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Locate the cell within it's page and leave pCell pointing to the
|
/* Locate the cell within it's page and leave pCell pointing to the
|
||||||
** data. The clearCell() call frees any overflow pages associated with the
|
** data. The clearCell() call frees any overflow pages associated with the
|
||||||
** cell. The cell itself is still intact.
|
** cell. The cell itself is still intact.
|
||||||
*/
|
*/
|
||||||
pCell = findCell(pPage, idx);
|
pCell = findCell(pPage, pCur->idx);
|
||||||
if( !pPage->leaf ){
|
if( !pPage->leaf ){
|
||||||
pgnoChild = get4byte(pCell);
|
pgnoChild = get4byte(pCell);
|
||||||
}
|
}
|
||||||
@@ -4527,120 +4333,48 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||||||
** do something we will leave a hole on an internal page.
|
** do something we will leave a hole on an internal page.
|
||||||
** We have to fill the hole by moving in a cell from a leaf. The
|
** We have to fill the hole by moving in a cell from a leaf. The
|
||||||
** next Cell after the one to be deleted is guaranteed to exist and
|
** next Cell after the one to be deleted is guaranteed to exist and
|
||||||
** to be a leaf so we can use it. Conveniantly, pCur now points
|
** to be a leaf so we can use it.
|
||||||
** at this cell (because it was advanced above).
|
|
||||||
*/
|
*/
|
||||||
BtCursor leafCur;
|
BtCursor leafCur;
|
||||||
unsigned char *pNext;
|
unsigned char *pNext;
|
||||||
int szNext;
|
int szNext;
|
||||||
|
int notUsed;
|
||||||
unsigned char *tempCell;
|
unsigned char *tempCell;
|
||||||
assert( !pPage->leafData );
|
assert( !pPage->leafData );
|
||||||
|
|
||||||
/* Make a copy of *pCur in leafCur. leafCur now points to the cell
|
|
||||||
** that will be moved into the space left by the cell being deleted.
|
|
||||||
*/
|
|
||||||
assert( pCur->delShift==1 );
|
|
||||||
assert( pCur->isValid );
|
|
||||||
getTempCursor(pCur, &leafCur);
|
getTempCursor(pCur, &leafCur);
|
||||||
|
rc = sqlite3BtreeNext(&leafCur, ¬Used);
|
||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
if( rc!=SQLITE_NOMEM ){
|
if( rc!=SQLITE_NOMEM ){
|
||||||
rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
|
rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
|
||||||
}
|
}
|
||||||
goto delete_out;
|
return rc;
|
||||||
}
|
}
|
||||||
rc = sqlite3pager_write(leafCur.pPage->aData);
|
rc = sqlite3pager_write(leafCur.pPage->aData);
|
||||||
if( rc ) goto delete_out;
|
if( rc ) return rc;
|
||||||
TRACE(("DELETE: table=%d delete internal from %d,%d replace "
|
TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
|
||||||
"from leaf %d,%d\n", pCur->pgnoRoot, pPage->pgno, idx,
|
pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
|
||||||
leafCur.pPage->pgno, leafCur.idx));
|
dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
|
||||||
|
|
||||||
/* Drop the cell from the internal page. Make a copy of the cell from
|
|
||||||
** the leaf page into memory obtained from malloc(). Insert it into
|
|
||||||
** the internal page, at the position vacated by the delete. There
|
|
||||||
** are now two copies of the leaf-cell in the tree.
|
|
||||||
*/
|
|
||||||
dropCell(pPage, idx, cellSizePtr(pPage, pCell));
|
|
||||||
pNext = findCell(leafCur.pPage, leafCur.idx);
|
pNext = findCell(leafCur.pPage, leafCur.idx);
|
||||||
szNext = cellSizePtr(leafCur.pPage, pNext);
|
szNext = cellSizePtr(leafCur.pPage, pNext);
|
||||||
assert( MX_CELL_SIZE(pBt)>=szNext+4 );
|
assert( MX_CELL_SIZE(pBt)>=szNext+4 );
|
||||||
tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
|
tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
|
||||||
if( tempCell==0 ){
|
if( tempCell==0 ) return SQLITE_NOMEM;
|
||||||
rc = SQLITE_NOMEM;
|
rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell);
|
||||||
goto delete_out;
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
}
|
put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
|
||||||
rc = insertCell(pPage, idx, pNext-4, szNext+4, tempCell);
|
|
||||||
if( rc!=SQLITE_OK ) goto delete_out;
|
|
||||||
put4byte(findOverflowCell(pPage, idx), pgnoChild);
|
|
||||||
pPage->idxShift = 0;
|
|
||||||
|
|
||||||
/* If there are any cursors that point to the leaf-cell, move them
|
|
||||||
** so that they point at internal cell. This is easiest done by
|
|
||||||
** calling BtreePrevious().
|
|
||||||
**
|
|
||||||
** Also, any cursors that point to the internal page have their
|
|
||||||
** cached parses invalidated, as the insertCell() above may have
|
|
||||||
** caused a defragmation.
|
|
||||||
*/
|
|
||||||
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
|
|
||||||
if( pCur2->pPage==leafCur.pPage && pCur2->idx==leafCur.idx ){
|
|
||||||
int res;
|
|
||||||
int delShiftSave = pCur2->delShift;
|
|
||||||
assert( leafCur.idx==0 );
|
|
||||||
pCur2->delShift = 0;
|
|
||||||
rc = sqlite3BtreePrevious(pCur2, &res);
|
|
||||||
if( rc ) goto delete_out;
|
|
||||||
assert( res==0 );
|
|
||||||
assert( pCur2->pPage==pPage );
|
|
||||||
assert( pCur2->idx==idx );
|
|
||||||
pCur2->delShift = delShiftSave;
|
|
||||||
}
|
|
||||||
if( pCur2->pPage==pPage ){
|
|
||||||
pCur2->info.nSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Balance the internal page. Free the memory allocated for the
|
|
||||||
** copy of the leaf cell. Then delete the cell from the leaf page.
|
|
||||||
*/
|
|
||||||
rc = balance(pPage);
|
rc = balance(pPage);
|
||||||
sqliteFree(tempCell);
|
sqliteFree(tempCell);
|
||||||
if( rc ) goto delete_out;
|
if( rc ) return rc;
|
||||||
dropCell(leafCur.pPage, leafCur.idx, szNext);
|
dropCell(leafCur.pPage, leafCur.idx, szNext);
|
||||||
|
|
||||||
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
|
|
||||||
if( pCur2->pPage==leafCur.pPage && pCur2->idx>leafCur.idx ){
|
|
||||||
TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n", pCur2,
|
|
||||||
leafCur.pPage->pgno,pCur2->idx,leafCur.pPage->pgno, pCur2->idx-1));
|
|
||||||
pCur2->idx--;
|
|
||||||
pCur2->info.nSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = balance(leafCur.pPage);
|
rc = balance(leafCur.pPage);
|
||||||
releaseTempCursor(&leafCur);
|
releaseTempCursor(&leafCur);
|
||||||
}else{
|
}else{
|
||||||
TRACE(("DELETE: table=%d delete %d from leaf %d\n",
|
TRACE(("DELETE: table=%d delete from leaf %d\n",
|
||||||
pCur->pgnoRoot, idx, pPage->pgno));
|
pCur->pgnoRoot, pPage->pgno));
|
||||||
dropCell(pPage, idx, cellSizePtr(pPage, pCell));
|
dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
|
||||||
|
|
||||||
/* If there were cursors pointing to cells on pPage with index values
|
|
||||||
** greater than idx, decrement the index values now.
|
|
||||||
*/
|
|
||||||
for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
|
|
||||||
assert( !pCur2->isValid || pCur2->pPage!=pPage || pCur2->idx!=idx );
|
|
||||||
if( pCur2->pPage==pPage && pCur2->idx>idx ){
|
|
||||||
TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n",
|
|
||||||
pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx-1));
|
|
||||||
pCur2->idx--;
|
|
||||||
pCur2->info.nSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = balance(pPage);
|
rc = balance(pPage);
|
||||||
}
|
}
|
||||||
|
moveToRoot(pCur);
|
||||||
delete_out:
|
|
||||||
releasePage(pPage);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
107
src/delete.c
107
src/delete.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle DELETE FROM statements.
|
** to handle DELETE FROM statements.
|
||||||
**
|
**
|
||||||
** $Id: delete.c,v 1.89 2004/11/16 15:50:20 danielk1977 Exp $
|
** $Id: delete.c,v 1.90 2004/11/22 10:02:10 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -64,6 +64,7 @@ void sqlite3OpenTableForReading(
|
|||||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
|
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Process a DELETE FROM statement.
|
** Process a DELETE FROM statement.
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +76,7 @@ void sqlite3DeleteFrom(
|
|||||||
Vdbe *v; /* The virtual database engine */
|
Vdbe *v; /* The virtual database engine */
|
||||||
Table *pTab; /* The table from which records will be deleted */
|
Table *pTab; /* The table from which records will be deleted */
|
||||||
const char *zDb; /* Name of database holding pTab */
|
const char *zDb; /* Name of database holding pTab */
|
||||||
int addr = 0; /* A couple addresses of generated code */
|
int end, addr = 0; /* A couple addresses of generated code */
|
||||||
int i; /* Loop counter */
|
int i; /* Loop counter */
|
||||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||||
Index *pIdx; /* For looping over indices of the table */
|
Index *pIdx; /* For looping over indices of the table */
|
||||||
@@ -149,15 +150,10 @@ void sqlite3DeleteFrom(
|
|||||||
oldIdx = pParse->nTab++;
|
oldIdx = pParse->nTab++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve the column names in all the expressions. Allocate cursors
|
/* Resolve the column names in all the expressions.
|
||||||
** for the table and indices first, in case an expression needs to use
|
|
||||||
** a cursor (e.g. an IN() expression).
|
|
||||||
*/
|
*/
|
||||||
assert( pTabList->nSrc==1 );
|
assert( pTabList->nSrc==1 );
|
||||||
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
||||||
pParse->nTab++;
|
|
||||||
}
|
|
||||||
if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){
|
if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){
|
||||||
goto delete_from_cleanup;
|
goto delete_from_cleanup;
|
||||||
}
|
}
|
||||||
@@ -231,6 +227,22 @@ void sqlite3DeleteFrom(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Begin the database scan
|
||||||
|
*/
|
||||||
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
|
||||||
|
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||||
|
|
||||||
|
/* Remember the key of every item to be deleted.
|
||||||
|
*/
|
||||||
|
sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0);
|
||||||
|
if( db->flags & SQLITE_CountRows ){
|
||||||
|
sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End the database scan loop.
|
||||||
|
*/
|
||||||
|
sqlite3WhereEnd(pWInfo);
|
||||||
|
|
||||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
@@ -238,49 +250,80 @@ void sqlite3DeleteFrom(
|
|||||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
|
sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open cursors for the table and indices we are deleting from. */
|
/* Delete every item whose key was written to the list during the
|
||||||
if( !isView ){
|
** database scan. We have to delete items after the scan is complete
|
||||||
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
|
** because deleting an item can change the scan order.
|
||||||
}
|
|
||||||
|
|
||||||
/* Begin the database scan
|
|
||||||
*/
|
*/
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, isView?0:iCur);
|
sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
end = sqlite3VdbeMakeLabel(v);
|
||||||
addr = pWInfo->iContinue;
|
|
||||||
|
|
||||||
/* If row-triggers exist, copy the record being deleted into the
|
/* This is the beginning of the delete loop when there are
|
||||||
** oldIdx psuedo-table. Then invoke the BEFORE triggers.
|
** row triggers.
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
|
addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
|
||||||
|
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
|
if( !isView ){
|
||||||
|
sqlite3OpenTableForReading(v, iCur, pTab);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
|
sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
|
sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||||
|
if( !isView ){
|
||||||
|
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||||
|
}
|
||||||
|
|
||||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
||||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||||
addr);
|
addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete the row. Increment the callback value if the count-rows flag
|
if( !isView ){
|
||||||
** is set.
|
/* Open cursors for the table we are deleting from and all its
|
||||||
*/
|
** indices. If there are row triggers, this happens inside the
|
||||||
if( db->flags & SQLITE_CountRows ){
|
** OP_ListRead loop because the cursor have to all be closed
|
||||||
sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
|
** before the trigger fires. If there are no row triggers, the
|
||||||
}
|
** cursors are opened only once on the outside the loop.
|
||||||
sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
|
*/
|
||||||
sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
|
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
|
||||||
|
|
||||||
/* Code the AFTER triggers. */
|
/* This is the beginning of the delete loop when there are no
|
||||||
|
** row triggers */
|
||||||
|
if( !row_triggers_exist ){
|
||||||
|
addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete the row */
|
||||||
|
sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there are row triggers, close all cursors then invoke
|
||||||
|
** the AFTER triggers
|
||||||
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
|
if( !isView ){
|
||||||
|
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||||
|
sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||||
|
}
|
||||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
||||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||||
addr);
|
addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End the database scan loop and close indices. */
|
/* End of the delete loop */
|
||||||
sqlite3WhereEnd(pWInfo);
|
sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
|
||||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
sqlite3VdbeResolveLabel(v, end);
|
||||||
sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
sqlite3VdbeAddOp(v, OP_ListReset, 0, 0);
|
||||||
|
|
||||||
|
/* Close the cursors after the loop if there are no row triggers */
|
||||||
|
if( !row_triggers_exist ){
|
||||||
|
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||||
|
sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle SELECT statements in SQLite.
|
** to handle SELECT statements in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: select.c,v 1.214 2004/11/16 15:50:20 danielk1977 Exp $
|
** $Id: select.c,v 1.215 2004/11/22 10:02:11 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -2523,7 +2523,7 @@ int sqlite3Select(
|
|||||||
/* Begin the database scan
|
/* Begin the database scan
|
||||||
*/
|
*/
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
|
||||||
pGroupBy ? 0 : &pOrderBy, -1);
|
pGroupBy ? 0 : &pOrderBy);
|
||||||
if( pWInfo==0 ) goto select_end;
|
if( pWInfo==0 ) goto select_end;
|
||||||
|
|
||||||
/* Use the standard inner loop if we are not dealing with
|
/* Use the standard inner loop if we are not dealing with
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.341 2004/11/20 19:18:01 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.342 2004/11/22 10:02:11 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITEINT_H_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@@ -1314,10 +1314,9 @@ void sqlite3SelectUnbind(Select*);
|
|||||||
Table *sqlite3SrcListLookup(Parse*, SrcList*);
|
Table *sqlite3SrcListLookup(Parse*, SrcList*);
|
||||||
int sqlite3IsReadOnly(Parse*, Table*, int);
|
int sqlite3IsReadOnly(Parse*, Table*, int);
|
||||||
void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*);
|
void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*);
|
||||||
void sqlite3OpenTable(Vdbe*, int iCur, Table*, int);
|
|
||||||
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
||||||
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**, int);
|
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**);
|
||||||
void sqlite3WhereEnd(WhereInfo*);
|
void sqlite3WhereEnd(WhereInfo*);
|
||||||
void sqlite3ExprCode(Parse*, Expr*);
|
void sqlite3ExprCode(Parse*, Expr*);
|
||||||
int sqlite3ExprCodeExprList(Parse*, ExprList*);
|
int sqlite3ExprCodeExprList(Parse*, ExprList*);
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle UPDATE statements.
|
** to handle UPDATE statements.
|
||||||
**
|
**
|
||||||
** $Id: update.c,v 1.95 2004/11/16 15:50:20 danielk1977 Exp $
|
** $Id: update.c,v 1.96 2004/11/22 10:02:11 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ void sqlite3Update(
|
|||||||
|
|
||||||
/* Begin the database scan
|
/* Begin the database scan
|
||||||
*/
|
*/
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0, -1);
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
|
||||||
if( pWInfo==0 ) goto update_cleanup;
|
if( pWInfo==0 ) goto update_cleanup;
|
||||||
|
|
||||||
/* Remember the index of every item to be updated.
|
/* Remember the index of every item to be updated.
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This module contains C code that generates VDBE code used to process
|
** This module contains C code that generates VDBE code used to process
|
||||||
** the WHERE clause of SQL statements.
|
** the WHERE clause of SQL statements.
|
||||||
**
|
**
|
||||||
** $Id: where.c,v 1.117 2004/11/16 15:50:20 danielk1977 Exp $
|
** $Id: where.c,v 1.118 2004/11/22 10:02:20 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -481,8 +481,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
SrcList *pTabList, /* A list of all tables to be scanned */
|
SrcList *pTabList, /* A list of all tables to be scanned */
|
||||||
Expr *pWhere, /* The WHERE clause */
|
Expr *pWhere, /* The WHERE clause */
|
||||||
int pushKey, /* If TRUE, leave the table key on the stack */
|
int pushKey, /* If TRUE, leave the table key on the stack */
|
||||||
ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
|
ExprList **ppOrderBy /* An ORDER BY clause, or NULL */
|
||||||
int iTabCur /* Cursor for pTabList->aSrc[0] */
|
|
||||||
){
|
){
|
||||||
int i; /* Loop counter */
|
int i; /* Loop counter */
|
||||||
WhereInfo *pWInfo; /* Will become the return value of this function */
|
WhereInfo *pWInfo; /* Will become the return value of this function */
|
||||||
@@ -778,9 +777,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
|
|
||||||
pTab = pTabList->a[i].pTab;
|
pTab = pTabList->a[i].pTab;
|
||||||
if( pTab->isTransient || pTab->pSelect ) continue;
|
if( pTab->isTransient || pTab->pSelect ) continue;
|
||||||
if( i>0 || iTabCur<0 ){
|
sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab);
|
||||||
sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab);
|
|
||||||
}
|
|
||||||
sqlite3CodeVerifySchema(pParse, pTab->iDb);
|
sqlite3CodeVerifySchema(pParse, pTab->iDb);
|
||||||
if( (pIx = pWInfo->a[i].pIdx)!=0 ){
|
if( (pIx = pWInfo->a[i].pIdx)!=0 ){
|
||||||
sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
|
sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
|
||||||
|
Reference in New Issue
Block a user