Changes to avoid loading large records into memory within VACUUM.
FossilOrigin-Name: 56a54258560fab715b83967634b2bd4c04be43cded112b46e85da9f99ee02f7c
This commit is contained in:
commit
a64804b5e1
28
manifest
28
manifest
@ -1,5 +1,5 @@
|
||||
C Add\san\soptional\sfunction\sto\sthe\schsumvfs\sextension.\s\sWhen\sactivated\sby\nthe\sSQLITE_CKSUMVFS_INIT_FUNCNAME\smacro,\sthis\sfunction\swill\sinvoke\sthe\nfile-control\sthat\ssets\sthe\snumber\sof\sreserved\sbytes\sto\s8.\s\sThis\scan\sbe\nused\sto\sinitialize\sa\scksumvfs\sdatabase\sfile\sby\sprogramming\slanguages\nthat\sdo\snot\shave\saccess\sto\sthe\ssqlite3_file_control()\sinterface.
|
||||
D 2020-12-10T13:49:00.833
|
||||
C Changes\sto\savoid\sloading\slarge\srecords\sinto\smemory\swithin\sVACUUM.
|
||||
D 2020-12-10T19:51:39.987
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -481,9 +481,9 @@ F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
|
||||
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c ee14224322b9e4172d01e691e2f289f6c630ae39b7906f84b72dc780b9e42a76
|
||||
F src/btree.h dcdff4037d75b3f032a5de0d922fcfaf35d48589417f634fa8627362709315f9
|
||||
F src/btreeInt.h ffd66480520d9d70222171b3a026d78b80833b5cea49c89867949f3e023d5f43
|
||||
F src/btree.c de8d2a4d8dedf9187cc9019b8f882bc788ab8fe815b7f69042e880fbc11304dc
|
||||
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
|
||||
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
|
||||
F src/build.c f6449d4e85e998e14d3f537e8ea898dca2fcb83c277db3e60945af9b9177db81
|
||||
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
|
||||
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 7e081d33aab4a9d761c39dccf3c3872c35501565d2ed9db66301918d23bc7901
|
||||
F src/insert.c 153c5b438d44ba0b8ae7ca467f8f6390b18958d14e681a26e5e861a2ac480750
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
|
||||
F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d
|
||||
@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009
|
||||
F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||
F src/sqliteInt.h 6b9e04aff5ae385e8d0957bf64cb3c1c42f4bf5115788c7be76e85ca9875740b
|
||||
F src/sqliteInt.h 6f99431f5b6f90948fb991907c35d11ca4e7eed43da48e790f8facd92c820711
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@ -612,7 +612,7 @@ F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
|
||||
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
||||
F src/vdbe.c d24a43b6b1ed2dba893636a14f5e56001444ab3fd5465e3bca8ab01799840acd
|
||||
F src/vdbe.c 808c1503dd183578d7ebcf0e517e636c6005f783daadc9a27305b10597c1142e
|
||||
F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
|
||||
F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
|
||||
F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
|
||||
@ -1650,10 +1650,11 @@ F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum-into.test 48f4cec354fb6f27c98ef58d2fe49a11b71ff131af0cd9140efacc9858b9f670
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b
|
||||
F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
|
||||
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
|
||||
F test/vacuum4.test 7ea76b769fffeb41f925303b04cbcf5a5bbeabe55e4c60ae754ff24eeeb7c010
|
||||
F test/vacuum5.test 263b144d537e92ad8e9ca8a73cc6e1583f41cfd0dda9432b87f7806174a2f48c
|
||||
F test/vacuum6.test e67488fa1341cc8034ba261807d429a8c06f4593ec1bb1651d8497d2d5df92c9
|
||||
F test/vacuummem.test 7b42abb3208bd82dd23a7536588396f295a314f2
|
||||
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
@ -1888,7 +1889,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P c0de6c1fb2c486be1da01e5e4ca8c5634ba37822e418d57f272e018c3e3fc0a2
|
||||
R 6c67d7c383f4b37d548756594c8d9b75
|
||||
U drh
|
||||
Z ffe92d1035962188772639969e0a3892
|
||||
P 01841fb4bf3d6c5fd5bcbc7d1338998c50f69f84ca475fba7cf764d636714678 f7fa76d0963e7b34026dc20c920bfbf7961033fe2b99503f6857157595f86823
|
||||
R 4dec8f74318b8721d36ac1bd02f7a778
|
||||
T +closed f7fa76d0963e7b34026dc20c920bfbf7961033fe2b99503f6857157595f86823
|
||||
U dan
|
||||
Z 3adf74c0105eb40ca1b5c5d7fdb48232
|
||||
|
@ -1 +1 @@
|
||||
01841fb4bf3d6c5fd5bcbc7d1338998c50f69f84ca475fba7cf764d636714678
|
||||
56a54258560fab715b83967634b2bd4c04be43cded112b46e85da9f99ee02f7c
|
143
src/btree.c
143
src/btree.c
@ -1143,6 +1143,24 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
|
||||
pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a record with nPayload bytes of payload stored within btree
|
||||
** page pPage, return the number of bytes of payload stored locally.
|
||||
*/
|
||||
static int btreePayloadToLocal(MemPage *pPage, int nPayload){
|
||||
int maxLocal; /* Maximum amount of payload held locally */
|
||||
maxLocal = pPage->maxLocal;
|
||||
if( nPayload<=maxLocal ){
|
||||
return nPayload;
|
||||
}else{
|
||||
int minLocal; /* Minimum amount of payload held locally */
|
||||
int surplus; /* Overflow payload available for local storage */
|
||||
minLocal = pPage->minLocal;
|
||||
surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
|
||||
return ( surplus <= maxLocal ) ? surplus : minLocal;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The following routines are implementations of the MemPage.xParseCell()
|
||||
** method.
|
||||
@ -8660,7 +8678,8 @@ int sqlite3BtreeInsert(
|
||||
unsigned char *oldCell;
|
||||
unsigned char *newCell = 0;
|
||||
|
||||
assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags );
|
||||
assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags );
|
||||
assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 );
|
||||
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
assert( pCur->skipNext!=SQLITE_OK );
|
||||
@ -8678,7 +8697,7 @@ int sqlite3BtreeInsert(
|
||||
** keys with no associated data. If the cursor was opened expecting an
|
||||
** intkey table, the caller should be inserting integer keys with a
|
||||
** blob of associated data. */
|
||||
assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
|
||||
assert( (pX->pKey==0)==(pCur->pKeyInfo==0) || (flags & BTREE_PREFORMAT) );
|
||||
|
||||
/* Save the positions of any other cursors open on this table.
|
||||
**
|
||||
@ -8788,7 +8807,7 @@ int sqlite3BtreeInsert(
|
||||
|| CORRUPT_DB );
|
||||
|
||||
pPage = pCur->pPage;
|
||||
assert( pPage->intKey || pX->nKey>=0 );
|
||||
assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
|
||||
assert( pPage->leaf || !pPage->intKey );
|
||||
if( pPage->nFree<0 ){
|
||||
if( pCur->eState>CURSOR_INVALID ){
|
||||
@ -8805,7 +8824,21 @@ int sqlite3BtreeInsert(
|
||||
assert( pPage->isInit );
|
||||
newCell = pBt->pTmpSpace;
|
||||
assert( newCell!=0 );
|
||||
rc = fillInCell(pPage, newCell, pX, &szNew);
|
||||
if( flags & BTREE_PREFORMAT ){
|
||||
rc = SQLITE_OK;
|
||||
szNew = pBt->nPreformatSize;
|
||||
if( szNew<4 ) szNew = 4;
|
||||
if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, newCell, &info);
|
||||
if( ISAUTOVACUUM && info.nPayload!=info.nLocal ){
|
||||
Pgno ovfl = get4byte(&newCell[szNew-4]);
|
||||
ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = fillInCell(pPage, newCell, pX, &szNew);
|
||||
}
|
||||
if( rc ) goto end_insert;
|
||||
assert( szNew==pPage->xCellSize(pPage, newCell) );
|
||||
assert( szNew <= MX_CELL_SIZE(pBt) );
|
||||
@ -8912,6 +8945,108 @@ end_insert:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used as part of copying the current row from cursor
|
||||
** pSrc into cursor pDest. If the cursors are open on intkey tables, then
|
||||
** parameter iKey is used as the rowid value when the record is copied
|
||||
** into pDest. Otherwise, the record is copied verbatim.
|
||||
**
|
||||
** This function does not actually write the new value to cursor pDest.
|
||||
** Instead, it creates and populates any required overflow pages and
|
||||
** writes the data for the new cell into the BtShared.pTmpSpace buffer
|
||||
** for the destination database. The size of the cell, in bytes, is left
|
||||
** in BtShared.nPreformatSize. The caller completes the insertion by
|
||||
** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
*/
|
||||
int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
int rc = SQLITE_OK;
|
||||
BtShared *pBt = pDest->pBt;
|
||||
u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
|
||||
const u8 *aIn; /* Pointer to next input buffer */
|
||||
int nIn; /* Size of input buffer aIn[] */
|
||||
int nRem; /* Bytes of data still to copy */
|
||||
|
||||
getCellInfo(pSrc);
|
||||
aOut += putVarint32(aOut, pSrc->info.nPayload);
|
||||
if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
|
||||
nIn = pSrc->info.nLocal;
|
||||
aIn = pSrc->info.pPayload;
|
||||
nRem = pSrc->info.nPayload;
|
||||
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
|
||||
memcpy(aOut, aIn, nIn);
|
||||
pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
|
||||
}else{
|
||||
Pager *pSrcPager = pSrc->pBt->pPager;
|
||||
u8 *pPgnoOut = 0;
|
||||
Pgno ovflIn = 0;
|
||||
DbPage *pPageIn = 0;
|
||||
MemPage *pPageOut = 0;
|
||||
int nOut; /* Size of output buffer aOut[] */
|
||||
|
||||
nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
|
||||
pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
|
||||
if( nOut<pSrc->info.nPayload ){
|
||||
pPgnoOut = &aOut[nOut];
|
||||
pBt->nPreformatSize += 4;
|
||||
}
|
||||
|
||||
if( nRem>nIn ){
|
||||
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
|
||||
}
|
||||
|
||||
do {
|
||||
nRem -= nOut;
|
||||
do{
|
||||
assert( nOut>0 );
|
||||
if( nIn>0 ){
|
||||
int nCopy = MIN(nOut, nIn);
|
||||
memcpy(aOut, aIn, nCopy);
|
||||
nOut -= nCopy;
|
||||
nIn -= nCopy;
|
||||
aOut += nCopy;
|
||||
aIn += nCopy;
|
||||
}
|
||||
if( nOut>0 ){
|
||||
sqlite3PagerUnref(pPageIn);
|
||||
pPageIn = 0;
|
||||
rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY);
|
||||
if( rc==SQLITE_OK ){
|
||||
aIn = (const u8*)sqlite3PagerGetData(pPageIn);
|
||||
ovflIn = get4byte(aIn);
|
||||
aIn += 4;
|
||||
nIn = pSrc->pBt->usableSize - 4;
|
||||
}
|
||||
}
|
||||
}while( rc==SQLITE_OK && nOut>0 );
|
||||
|
||||
if( rc==SQLITE_OK && nRem>0 ){
|
||||
Pgno pgnoNew;
|
||||
MemPage *pNew = 0;
|
||||
rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
|
||||
put4byte(pPgnoOut, pgnoNew);
|
||||
if( ISAUTOVACUUM && pPageOut ){
|
||||
ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
|
||||
}
|
||||
releasePage(pPageOut);
|
||||
pPageOut = pNew;
|
||||
if( pPageOut ){
|
||||
pPgnoOut = pPageOut->aData;
|
||||
put4byte(pPgnoOut, 0);
|
||||
aOut = &pPgnoOut[4];
|
||||
nOut = MIN(pBt->usableSize - 4, nRem);
|
||||
}
|
||||
}
|
||||
}while( nRem>0 && rc==SQLITE_OK );
|
||||
|
||||
releasePage(pPageOut);
|
||||
sqlite3PagerUnref(pPageIn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the entry that the cursor is pointing to.
|
||||
**
|
||||
|
@ -262,6 +262,7 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
#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 */
|
||||
#define BTREE_PREFORMAT 0x80 /* 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.
|
||||
@ -361,6 +362,8 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
|
||||
#endif
|
||||
|
||||
int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);
|
||||
|
||||
/*
|
||||
** If we are not using shared cache, then there is no need to
|
||||
** use mutexes to access the BtShared structures. So make the
|
||||
|
@ -455,6 +455,7 @@ struct BtShared {
|
||||
Btree *pWriter; /* Btree with currently open write transaction */
|
||||
#endif
|
||||
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
|
||||
int nPreformatSize; /* Size of last cell written by TransferRow() */
|
||||
};
|
||||
|
||||
/*
|
||||
|
38
src/insert.c
38
src/insert.c
@ -2748,6 +2748,7 @@ static int xferOptimization(
|
||||
iDest = pParse->nTab++;
|
||||
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
|
||||
regData = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regData);
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
|
||||
assert( HasRowid(pDest) || destHasUniqueIdx );
|
||||
@ -2783,11 +2784,13 @@ static int xferOptimization(
|
||||
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
if( pDest->iPKey>=0 ){
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3RowidConstraint(pParse, onError, pDest);
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3RowidConstraint(pParse, onError, pDest);
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
}
|
||||
autoIncStep(pParse, regAutoinc, regRowid);
|
||||
}else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
|
||||
@ -2795,16 +2798,26 @@ static int xferOptimization(
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
|
||||
}else{
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( db->xPreUpdateCallback ){
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
insFlags &= ~OPFLAG_PREFORMAT;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
(char*)pDest, P4_TABLE);
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, insFlags);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
|
||||
@ -2846,13 +2859,16 @@ static int xferOptimization(
|
||||
if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
|
||||
}
|
||||
if( i==pSrcIdx->nColumn ){
|
||||
idxInsFlags = OPFLAG_USESEEKRESULT;
|
||||
idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regData);
|
||||
}
|
||||
}else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
|
||||
idxInsFlags |= OPFLAG_NCHANGE;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
|
||||
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
|
||||
|
@ -3529,6 +3529,7 @@ struct AuthContext {
|
||||
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */
|
||||
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
|
||||
#define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */
|
||||
#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
|
32
src/vdbe.c
32
src/vdbe.c
@ -5126,7 +5126,8 @@ case OP_Insert: {
|
||||
}
|
||||
x.pKey = 0;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
|
||||
seekResult
|
||||
);
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
@ -5143,6 +5144,31 @@ case OP_Insert: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: RowCell P1 P2 P3 * *
|
||||
**
|
||||
** P1 and P2 are both open cursors. Both must be opened on the same type
|
||||
** of table - intkey or index. This opcode is used as part of copying
|
||||
** the current row from P2 into P1. If the cursors are opened on intkey
|
||||
** tables, register P3 contains the rowid to use with the new record in
|
||||
** P1. If they are opened on index tables, P3 is not used.
|
||||
**
|
||||
** This opcode must be followed by either an Insert or InsertIdx opcode
|
||||
** with the OPFLAG_PREFORMAT flag set to complete the insert operation.
|
||||
*/
|
||||
case OP_RowCell: {
|
||||
VdbeCursor *pDest; /* Cursor to write to */
|
||||
VdbeCursor *pSrc; /* Cursor to read from */
|
||||
i64 iKey; /* Rowid value to insert with */
|
||||
assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert );
|
||||
assert( pOp[1].p5 & OPFLAG_PREFORMAT );
|
||||
pDest = p->apCsr[pOp->p1];
|
||||
pSrc = p->apCsr[pOp->p2];
|
||||
iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0;
|
||||
rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
break;
|
||||
};
|
||||
|
||||
/* Opcode: Delete P1 P2 P3 P4 P5
|
||||
**
|
||||
** Delete the record at which the P1 cursor is currently pointing.
|
||||
@ -5798,7 +5824,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
assert( pC!=0 );
|
||||
assert( !isSorter(pC) );
|
||||
pIn2 = &aMem[pOp->p2];
|
||||
assert( pIn2->flags & MEM_Blob );
|
||||
assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) );
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( pC->isTable==0 );
|
||||
@ -5809,7 +5835,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|OPFLAG_SAVEPOSITION)),
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
|
@ -56,7 +56,7 @@ do_test vacuum2-2.1 {
|
||||
}
|
||||
hexio_get_int [hexio_read test.db 24 4]
|
||||
} [expr {[hexio_get_int [hexio_read test.db 24 4]]+3}]
|
||||
do_test vacuum2-2.1 {
|
||||
do_test vacuum2-2.2 {
|
||||
execsql {
|
||||
VACUUM
|
||||
}
|
||||
|
101
test/vacuum6.test
Normal file
101
test/vacuum6.test
Normal file
@ -0,0 +1,101 @@
|
||||
# 2016-08-19
|
||||
#
|
||||
# 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 a test for VACUUM on attached databases.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix vacuum6
|
||||
|
||||
# If the VACUUM statement is disabled in the current build, skip all
|
||||
# the tests in this file.
|
||||
#
|
||||
ifcapable !vacuum {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
} {}
|
||||
do_execsql_test 1.1 {
|
||||
VACUUM
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
foreach {tn sz} {1 400 2 4000 3 9999} {
|
||||
reset_db
|
||||
do_execsql_test 2.$tn.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO t1 SELECT i, randomblob($sz) FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 2.$tn.2 {
|
||||
vacuum;
|
||||
}
|
||||
|
||||
do_execsql_test 2.$tn.3 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t1 VALUES(2, randomblob(1200));
|
||||
} {}
|
||||
do_execsql_test 3.1 {
|
||||
PRAGMA page_size = 512;
|
||||
VACUUM;
|
||||
}
|
||||
do_execsql_test 3.2 {
|
||||
PRAGMA integrity_check
|
||||
} {ok}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE tx(a, b);
|
||||
CREATE INDEX i1 ON tx(b);
|
||||
WITH s(i) AS (
|
||||
SELECT 8000 UNION ALL SELECT i+1 FROM s WHERE i<10000
|
||||
)
|
||||
INSERT INTO tx SELECT i, randomblob(i) FROM s;
|
||||
|
||||
SELECT sum(length(b)) FROM tx;
|
||||
} {18009000}
|
||||
foreach {tn pgsz av} {
|
||||
1 2048 0
|
||||
2 1024 1
|
||||
3 65536 0
|
||||
4 8192 1
|
||||
5 512 0
|
||||
6 4096 1
|
||||
} {
|
||||
do_execsql_test 4.1.$tn.1 "
|
||||
PRAGMA page_size = $pgsz;
|
||||
PRAGMA auto_vacuum = $av;
|
||||
"
|
||||
do_execsql_test 4.1.$tn.2 VACUUM
|
||||
integrity_check 4.1.$tn.3
|
||||
}
|
||||
|
||||
finish_test
|
Loading…
Reference in New Issue
Block a user