From ade8954c40a5b117d9d28347b7ea3eff44fbc82c Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 24 Jun 2026 11:50:08 +0000 Subject: [PATCH] Revert the fixes to backup in [1f940357f7] and [e5db80350c] and instead fix the problem reported by forum post [forum:15d82885e2 | 15d82885e2] (that a call to sqlite3_deserialize() after sqlite3_backup_init() but before the first call to sqlite3_backup_step() on the destination db of a backup could cause a crash) by deferring caching the pointer to the destination Btree until after it is locked. This addresses bug [bugs:/info/2026-06-24T08:41:13Z | 2026-06-24T08:41:13Z]. FossilOrigin-Name: 44a0f396daa13f382fd839cd8be49148aa24114968b97fed7f365307d1f8280a --- manifest | 25 +++--- manifest.tags | 4 +- manifest.uuid | 2 +- src/backup.c | 189 +++++++++++++++++++++++----------------------- src/btree.h | 2 +- src/build.c | 1 + src/vacuum.c | 4 +- test/backup5.test | 49 ++++++++++-- 8 files changed, 157 insertions(+), 119 deletions(-) diff --git a/manifest b/manifest index 1c86e1dc7c..9cf33c6e2c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Swap\stwo\slines\sto\sget\sthe\samalgamation\sbuilding\sagain\swith\sSQLITE_OMIT_FLOATING_POINT,\snoting\sthat\sneither\sthe\sshell\snor\sthe\scanonical\slibrary\sbuild\swith\sthat\sflag.\sReported\sin\s[forum:8c3df4da0d|forum\spost\s2026-06-24T07:21:05Z]. -D 2026-06-24T10:22:36.339 +C Revert\sthe\sfixes\sto\sbackup\sin\s[1f940357f7]\sand\s[e5db80350c]\sand\sinstead\sfix\sthe\sproblem\sreported\sby\sforum\spost\s[forum:15d82885e2\s|\s15d82885e2]\s(that\sa\scall\sto\ssqlite3_deserialize()\safter\ssqlite3_backup_init()\sbut\sbefore\sthe\sfirst\scall\sto\ssqlite3_backup_step()\son\sthe\sdestination\sdb\sof\sa\sbackup\scould\scause\sa\scrash)\sby\sdeferring\scaching\sthe\spointer\sto\sthe\sdestination\sBtree\suntil\safter\sit\sis\slocked.\sThis\saddresses\sbug\s[bugs:/info/2026-06-24T08:41:13Z\s|\s2026-06-24T08:41:13Z]. +D 2026-06-24T11:50:08.765 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -674,13 +674,13 @@ F src/alter.c da59ac700b52ba5d0e4dd099fb1818975cf8a79a546594da586b4e1eba3ae405 F src/analyze.c 73162482c656187823217f4c00758c9ee13a420c8745bc542129e0279b792287 F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338 F src/auth.c b5ece4e1edccad082c0332fa0087df225473bae0feea9269f824312201377185 -F src/backup.c 2f5f350c714407a04fadc9c812b8fcbb47d472c543c492f9fc03043c94255406 +F src/backup.c 89de631678bcbb3ad46f8a8bb43fe4b87b8ada42accd1fe5def363d352ac26d3 F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399 F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea F src/btree.c 515cf62220ceb483ba9a31ebb3d7565ea9d63ffc3d61bb974b2815fef393df0e -F src/btree.h c3ba6ba663e185b265a327165bd795e6dc7806fe5a75fe9f547a8459c96f73a7 +F src/btree.h 2ee0ddfdf4f8530ad1d46afffd7da21a0e243bfab10973011ac6f6b7fb4109a1 F src/btreeInt.h 4f512ad31083216b6789762d4c345b73367985d3b39421c9ba7c0902d09fb38b -F src/build.c 09946336c3011c2ae2faccdf04e33336e1cd51fd836651be0cd7eb5814f7f6a0 +F src/build.c 0a06d413b17a5d7ff80a8499c413e7e3e50cd96f161c4d400462cb8416698c06 F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad F src/carray.c 3efe3982d5fb323334c29328a4e189ccaef6b95612a6084ad5fa124fd5db1179 F src/complete.c f216b970ce99c5a657556cf1f17e7ddd494515d3beb63df426bf59ff43bd3d9a @@ -803,7 +803,7 @@ F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf F src/upsert.c dd9f0fcccbfb4f20e1026a21a7254ba3f2c08e9cfa92affaff5b5ec3b00ea549 F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165 F src/util.c c069caa2e85389deeda302b792161dc536d50c401dd447269e4934aca8f6790c -F src/vacuum.c 4eda73a75a15f939b45b26e189f5b25757b39d7cb6d5e972a7f754e75d019189 +F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82 F src/vdbe.c 6397694fa506aa1841dc8bb6a17c514aa602a4ad2515024fcd5880558c1ef57f F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71 F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1 @@ -899,7 +899,7 @@ F test/backcompat.test f2431465ed668f09fc3f6998e56e893a1506ccea6e8b6f409f085f759 F test/backup.test 3b08fd4af69f0fa786931103a31f4542b184aba16e239e5f22b18c3c2476697f F test/backup2.test b153553ee5667b0748b43346b0725fbf80ce1f5544613bf087d669778b60ec56 F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32 -F test/backup5.test 3e16065dc45c40030378702aac3e92f4d6e7d8853f0328f0390c9e7a71d8c712 +F test/backup5.test a0716934a41bea622c5f1928d00c23093e986c3dfc58aaca9db3b431c66276dc F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135 F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027 F test/badutf.test cff75b714866a4ffa0cdda252eb8fe8765483f5872c0076223c92d52b4fffd1b @@ -2208,8 +2208,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P e458ed9cc9d46dfdade3b4e99eb209d8aefd40a245e002d68df1813f9f99e2d7 -R 592bd4e6bc47a54bee1f0be621a913e3 -U stephan -Z 56bbc5d496907534eef40aa7c99dc40d +P 23936786e680131af644510eb50b7ad20ee39221d1fb29732dea76483aed2273 +R e0e0f7b38815f812e27f687f577ea44f +T *branch * attach-backup-fix +T *sym-attach-backup-fix * +T -sym-trunk * +U dan +Z df2c9c70dcdd169082f93f43b6437c36 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..5932301b49 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch attach-backup-fix +tag attach-backup-fix diff --git a/manifest.uuid b/manifest.uuid index 6f73e7e51e..b60937e3a0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -23936786e680131af644510eb50b7ad20ee39221d1fb29732dea76483aed2273 +44a0f396daa13f382fd839cd8be49148aa24114968b97fed7f365307d1f8280a diff --git a/src/backup.c b/src/backup.c index 0a20ae4cf9..c46d896293 100644 --- a/src/backup.c +++ b/src/backup.c @@ -20,13 +20,14 @@ */ struct sqlite3_backup { sqlite3* pDestDb; /* Destination database handle */ - int iDest; /* Destination db file */ + char *zDestDb; + Btree *pDest; /* Destination b-tree file */ u32 iDestSchema; /* Original schema cookie in destination */ int bDestLocked; /* True once a write-transaction is open on pDest */ Pgno iNext; /* Page number of the next source page to copy */ sqlite3* pSrcDb; /* Source database handle */ - int iSrc; /* Source db file */ + Btree *pSrc; /* Source b-tree file */ int rc; /* Backup process error code */ @@ -40,20 +41,6 @@ struct sqlite3_backup { sqlite3_backup *pNext; /* Next backup associated with source pager */ }; -/* -** Return the source (Btree*) used by backup object b. -*/ -#define getSourceBtree(b) ((b)->pSrcDb->aDb[(b)->iSrc].pBt) - -/* -** Return the destination (Btree*) used by backup object b. -*/ -#define getDestBtree(b) ( (b)->pDestDb ? \ - (b)->pDestDb->aDb[(b)->iDest].pBt : \ - (b)->pSrcDb->aDb[(b)->iDest].pBt \ -) - - /* ** THREAD SAFETY NOTES: ** @@ -85,15 +72,15 @@ struct sqlite3_backup { */ /* -** Return an integer corresponding to database zDb (i.e. "main", "temp") +** Return a pointer corresponding to database zDb (i.e. "main", "temp") ** in connection handle pDb. If such a database cannot be found, return -** a negative value and write an error message to pErrorDb. +** a NULL pointer and write an error message to pErrorDb. ** ** If the "temp" database is requested, it may need to be opened by this -** function. If an error occurs while doing so, return -1 and write an +** function. If an error occurs while doing so, return 0 and write an ** error message to pErrorDb. */ -static int findDatabase(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ +static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ int i = sqlite3FindDbName(pDb, zDb); if( i==1 ){ @@ -107,25 +94,24 @@ static int findDatabase(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ sqlite3DbFree(pErrorDb, sParse.zErrMsg); sqlite3ParseObjectReset(&sParse); if( rc ){ - return -1; + return 0; } } if( i<0 ){ sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); + return 0; } - return i; + return pDb->aDb[i].pBt; } /* ** Attempt to set the page size of the destination to match the page size ** of the source. */ -static int setDestPgsz(sqlite3_backup *p){ - return sqlite3BtreeSetPageSize(getDestBtree(p), - sqlite3BtreeGetPageSize(getSourceBtree(p)), 0, 0 - ); +static int setDestPgsz(Btree *pDest, Btree *pSrc){ + return sqlite3BtreeSetPageSize(pDest, sqlite3BtreeGetPageSize(pSrc), 0, 0); } /* @@ -182,27 +168,37 @@ sqlite3_backup *sqlite3_backup_init( ); p = 0; }else { + int nDest = sqlite3Strlen30(zDestDb); + /* Allocate space for a new sqlite3_backup object... ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ - p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup)); + p = (sqlite3_backup*)sqlite3MallocZero(sizeof(sqlite3_backup)+nDest+1); if( !p ){ sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT); + }else{ + p->zDestDb = (char*)&p[1]; + memcpy(p->zDestDb, zDestDb, nDest); } } /* If the allocation succeeded, populate the new object. */ if( p ){ - p->iDest = findDatabase(pDestDb, pDestDb, zDestDb); - p->iSrc = findDatabase(pDestDb, pSrcDb, zSrcDb); + /* Do not store the pointer to the destination b-tree at this point. + ** This is because there is nothing preventing it from being detached + ** or otherwise freed before the first call to sqlite3_backup_step() + ** on this object. The source b-tree does not have this problem, as + ** incrementing Btree.nBackup (see below) effectively locks the object. */ + Btree *pDest = findBtree(pDestDb, pDestDb, zDestDb); + p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); p->pDestDb = pDestDb; p->pSrcDb = pSrcDb; p->iNext = 1; p->isAttached = 0; - if( p->iSrc<0 || p->iDest<0 - || checkReadTransaction(pDestDb, getDestBtree(p))!=SQLITE_OK + if( 0==p->pSrc || 0==pDest + || checkReadTransaction(pDestDb, pDest)!=SQLITE_OK ){ /* One (or both) of the named databases did not exist or an OOM ** error was hit. Or there is a transaction open on the destination @@ -214,7 +210,7 @@ sqlite3_backup *sqlite3_backup_init( } } if( p ){ - getSourceBtree(p)->nBackup++; + p->pSrc->nBackup++; } sqlite3_mutex_leave(pDestDb->mutex); @@ -242,19 +238,18 @@ static int backupOnePage( const u8 *zSrcData, /* Source database page data */ int bUpdate /* True for an update, false otherwise */ ){ - Btree *pDestBt = getDestBtree(p); - Pager * const pDestPager = sqlite3BtreePager(pDestBt); - const int nSrcPgsz = sqlite3BtreeGetPageSize(getSourceBtree(p)); - int nDestPgsz = sqlite3BtreeGetPageSize(pDestBt); + Pager * const pDestPager = sqlite3BtreePager(p->pDest); + const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); + int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; int rc = SQLITE_OK; i64 iOff; - assert( sqlite3BtreeGetReserveNoMutex(getSourceBtree(p))>=0 ); + assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); assert( p->bDestLocked ); assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(getSourceBtree(p)->pBt) ); + assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); assert( zSrcData ); assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); @@ -265,7 +260,7 @@ static int backupOnePage( for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpBt) ) continue; + if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue; if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0)) && SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg)) ){ @@ -283,7 +278,7 @@ static int backupOnePage( memcpy(zOut, zIn, nCopy); ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; if( iOff==0 && bUpdate==0 ){ - sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(getSourceBtree(p))); + sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc)); } } sqlite3PagerUnref(pDestPg); @@ -315,8 +310,8 @@ static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){ */ static void attachBackupObject(sqlite3_backup *p){ sqlite3_backup **pp; - assert( sqlite3BtreeHoldsMutex(getSourceBtree(p)) ); - pp = sqlite3PagerBackupPtr(sqlite3BtreePager(getSourceBtree(p))); + assert( sqlite3BtreeHoldsMutex(p->pSrc) ); + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); p->pNext = *pp; *pp = p; p->isAttached = 1; @@ -330,26 +325,21 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ int pgszDest = 0; /* Destination page size */ - Btree *pDest; - Btree *pSrc; #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ) return SQLITE_MISUSE_BKPT; #endif - assert( p->iDest>=0 ); - assert( p->iSrc>=0 ); - pDest = getDestBtree(p); - pSrc = getSourceBtree(p); sqlite3_mutex_enter(p->pSrcDb->mutex); - sqlite3BtreeEnter(pSrc); + sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } rc = p->rc; if( !isFatalError(rc) ){ - Pager * const pSrcPager = sqlite3BtreePager(pSrc); /* Source pager */ - Pager * const pDestPager = sqlite3BtreePager(pDest); /* Dest pager */ + Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ + Btree * pDest = 0; /* Dest btree */ + Pager * pDestPager = 0; /* Dest pager */ int ii; /* Iterator variable */ int nSrcPage = -1; /* Size of source db in pages */ int bCloseTrans = 0; /* True if src db requires unlocking */ @@ -357,28 +347,41 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* If the source pager is currently in a write-transaction, return ** SQLITE_BUSY immediately. */ - if( p->pDestDb && pSrc->pBt->inTransaction==TRANS_WRITE ){ + if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ rc = SQLITE_BUSY; }else{ rc = SQLITE_OK; } + /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. */ - if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(pSrc) ){ - rc = sqlite3BtreeBeginTrans(pSrc, 0, 0); + if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){ + rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0); bCloseTrans = 1; } + /* Locate the destination btree and pager. */ + if( (pDest = p->pDest)==0 ){ + pDest = findBtree(p->pDestDb, p->pDestDb, p->zDestDb); + } + if( pDest==0 ){ + rc = SQLITE_ERROR; + }else{ + pDestPager = sqlite3BtreePager(pDest); + } + /* If the destination database has not yet been locked (i.e. if this ** is the first call to backup_step() for the current backup operation), ** try to set its page size to the same as the source database. This ** is especially important on ZipVFS systems, as in that case it is ** not possible to create a database file that uses one page size by ** writing to it with another. */ - if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){ + if( p->bDestLocked==0 && rc==SQLITE_OK + && setDestPgsz(pDest, p->pSrc)==SQLITE_NOMEM + ){ rc = SQLITE_NOMEM; } @@ -388,28 +391,30 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ (int*)&p->iDestSchema)) ){ p->bDestLocked = 1; + p->pDest = pDest; } /* Do not allow backup if the destination database is in WAL mode ** and the page sizes are different between source and destination */ - pgszSrc = sqlite3BtreeGetPageSize(pSrc); - pgszDest = sqlite3BtreeGetPageSize(pDest); - destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(pDest)); - if( SQLITE_OK==rc - && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) - && pgszSrc!=pgszDest - ){ - rc = SQLITE_READONLY; + if( rc==SQLITE_OK ){ + pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); + pgszDest = sqlite3BtreeGetPageSize(p->pDest); + destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); + if( (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) + && pgszSrc!=pgszDest + ){ + rc = SQLITE_READONLY; + } } /* Now that there is a read-lock on the source database, query the ** source pager for the number of pages in the database. */ - nSrcPage = (int)sqlite3BtreeLastPage(pSrc); + nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc); assert( nSrcPage>=0 ); for(ii=0; (nPage<0 || iiiNext<=(Pgno)nSrcPage && !rc; ii++){ const Pgno iSrcPg = p->iNext; /* Source page number */ - if( iSrcPg!=PENDING_BYTE_PAGE(pSrc->pBt) ){ + if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ DbPage *pSrcPg; /* Source page object */ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY); if( rc==SQLITE_OK ){ @@ -436,18 +441,18 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ */ if( rc==SQLITE_DONE ){ if( nSrcPage==0 ){ - rc = sqlite3BtreeNewDb(pDest); + rc = sqlite3BtreeNewDb(p->pDest); nSrcPage = 1; } if( rc==SQLITE_OK || rc==SQLITE_DONE ){ - rc = sqlite3BtreeUpdateMeta(pDest,1,p->iDestSchema+1); + rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); } if( rc==SQLITE_OK ){ if( p->pDestDb ){ sqlite3ResetAllSchemasOfConnection(p->pDestDb); } if( destMode==PAGER_JOURNALMODE_WAL ){ - rc = sqlite3BtreeSetVersion(pDest, 2); + rc = sqlite3BtreeSetVersion(p->pDest, 2); } } if( rc==SQLITE_OK ){ @@ -464,12 +469,12 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ ** journalled by PagerCommitPhaseOne() before they are destroyed ** by the file truncation. */ - assert( pgszSrc==sqlite3BtreeGetPageSize(pSrc) ); - assert( pgszDest==sqlite3BtreeGetPageSize(pDest) ); + assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) ); + assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) ); if( pgszSrcpBt) ){ + if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){ nDestTruncate--; } }else{ @@ -497,7 +502,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ assert( pFile ); assert( nDestTruncate==0 || (i64)nDestTruncate*(i64)pgszDest >= iSize || ( - nDestTruncate==(int)(PENDING_BYTE_PAGE(pDest->pBt)-1) + nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); @@ -509,7 +514,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ ** journal file. */ sqlite3PagerPagecount(pDestPager, &nDstPage); for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ - if( iPg!=PENDING_BYTE_PAGE(pDest->pBt) ){ + if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ DbPage *pPg; rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0); if( rc==SQLITE_OK ){ @@ -553,7 +558,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(pDest, 0)) + && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) ){ rc = SQLITE_DONE; } @@ -567,8 +572,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ */ if( bCloseTrans ){ TESTONLY( int rc2 ); - TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(pSrc, 0); - TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(pSrc, 0); + TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); + TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); assert( rc2==SQLITE_OK ); } @@ -580,7 +585,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } - sqlite3BtreeLeave(pSrc); + sqlite3BtreeLeave(p->pSrc); sqlite3_mutex_leave(p->pSrcDb->mutex); return rc; } @@ -592,24 +597,22 @@ int sqlite3_backup_finish(sqlite3_backup *p){ sqlite3_backup **pp; /* Ptr to head of pagers backup list */ sqlite3 *pSrcDb; /* Source database connection */ int rc; /* Value to return */ - Btree *pSrcBt = 0; /* Enter the mutexes */ if( p==0 ) return SQLITE_OK; - pSrcBt = getSourceBtree(p); pSrcDb = p->pSrcDb; sqlite3_mutex_enter(pSrcDb->mutex); - sqlite3BtreeEnter(pSrcBt); + sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } /* Detach this backup from the source pager. */ if( p->pDestDb ){ - pSrcBt->nBackup--; + p->pSrc->nBackup--; } if( p->isAttached ){ - pp = sqlite3PagerBackupPtr(sqlite3BtreePager(pSrcBt)); + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); assert( pp!=0 ); while( *pp!=p ){ pp = &(*pp)->pNext; @@ -619,7 +622,9 @@ int sqlite3_backup_finish(sqlite3_backup *p){ } /* If a transaction is still open on the Btree, roll it back. */ - sqlite3BtreeRollback(getDestBtree(p), SQLITE_OK, 0); + if( p->pDest ){ + sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0); + } /* Set the error code of the destination database handle. */ rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; @@ -629,7 +634,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){ /* Exit the mutexes and free the backup context structure. */ sqlite3LeaveMutexAndCloseZombie(p->pDestDb); } - sqlite3BtreeLeave(pSrcBt); + sqlite3BtreeLeave(p->pSrc); if( p->pDestDb ){ /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to @@ -687,7 +692,7 @@ static SQLITE_NOINLINE void backupUpdate( ){ assert( p!=0 ); do{ - assert( sqlite3_mutex_held(getSourceBtree(p)->pBt->mutex) ); + assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); if( !isFatalError(p->rc) && iPageiNext ){ /* The backup process p has already copied page iPage. But now it ** has been modified by a transaction on the source pager. Copy @@ -723,7 +728,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ void sqlite3BackupRestart(sqlite3_backup *pBackup){ sqlite3_backup *p; /* Iterator variable */ for(p=pBackup; p; p=p->pNext){ - assert( sqlite3_mutex_held(getSourceBtree(p)->pBt->mutex) ); + assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); p->iNext = 1; } } @@ -737,12 +742,10 @@ void sqlite3BackupRestart(sqlite3_backup *pBackup){ ** goes wrong, the transaction on pTo is rolled back. If successful, the ** transaction is committed before returning. */ -int sqlite3BtreeCopyFile(sqlite3 *db, int iTo, int iFrom){ +int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ int rc; sqlite3_file *pFd; /* File descriptor for database pTo */ sqlite3_backup b; - Btree *pFrom = db->aDb[iFrom].pBt; - Btree *pTo = db->aDb[iTo].pBt; sqlite3BtreeEnter(pTo); sqlite3BtreeEnter(pFrom); @@ -761,9 +764,9 @@ int sqlite3BtreeCopyFile(sqlite3 *db, int iTo, int iFrom){ ** from this function, not directly by the user. */ memset(&b, 0, sizeof(b)); - b.pSrcDb = db; - b.iSrc = iFrom; - b.iDest = iTo; + b.pSrcDb = pFrom->db; + b.pSrc = pFrom; + b.pDest = pTo; b.iNext = 1; /* 0x7FFFFFFF is the hard limit for the number of pages in a database @@ -779,7 +782,7 @@ int sqlite3BtreeCopyFile(sqlite3 *db, int iTo, int iFrom){ if( rc==SQLITE_OK ){ pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; }else{ - sqlite3PagerClearCache(sqlite3BtreePager(pTo)); + sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); } assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); diff --git a/src/btree.h b/src/btree.h index ba9a540eb6..043056e32b 100644 --- a/src/btree.h +++ b/src/btree.h @@ -105,7 +105,7 @@ int sqlite3BtreeSavepoint(Btree *, int, int); const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); -int sqlite3BtreeCopyFile(sqlite3*, int, int); +int sqlite3BtreeCopyFile(Btree*, Btree*); int sqlite3BtreeIncrVacuum(Btree *); diff --git a/src/build.c b/src/build.c index 7c6b818dba..43b8be8683 100644 --- a/src/build.c +++ b/src/build.c @@ -608,6 +608,7 @@ void sqlite3CollapseDatabaseArray(sqlite3 *db){ } if( jaDb[j] = db->aDb[i]; +// sqlite3BackupUpdateSrc(db, j); } j++; } diff --git a/src/vacuum.c b/src/vacuum.c index 94de364de7..70e62e1ef1 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -376,9 +376,7 @@ SQLITE_NOINLINE int sqlite3RunVacuum( } if( pOut==0 ){ - assert( pMain==db->aDb[iDb].pBt ); - assert( pTemp==db->aDb[nDb].pBt ); - rc = sqlite3BtreeCopyFile(db, iDb, nDb); + rc = sqlite3BtreeCopyFile(pMain, pTemp); } if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); diff --git a/test/backup5.test b/test/backup5.test index e68a258c3d..39092cc494 100644 --- a/test/backup5.test +++ b/test/backup5.test @@ -65,7 +65,9 @@ do_test 1.7 { #------------------------------------------------------------------------- reset_db -forcedelete test2.db +foreach n {test2.db test3.db test4.db test5.db test6.db test7.db test8.db} { + forcedelete $n +} do_execsql_test 2.0 { CREATE TABLE t1(a, b); @@ -75,7 +77,7 @@ do_execsql_test 2.0 { INSERT INTO t2 VALUES(3, 3); CREATE INDEX i1 ON t1(a); CREATE INDEX i2 ON t2(a); - ATTACH 'test.db2' AS aux2; + ATTACH 'test2.db' AS aux2; } sqlite3 db2 test2.db @@ -85,17 +87,48 @@ do_test 2.1 { B step 1 execsql { - ATTACH 'test.db3' AS aux3; - ATTACH 'test.db4' AS aux4; - ATTACH 'test.db5' AS aux5; - ATTACH 'test.db6' AS aux6; - ATTACH 'test.db7' AS aux7; - ATTACH 'test.db8' AS aux8; + ATTACH 'test3.db' AS aux3; + ATTACH 'test4.db' AS aux4; + ATTACH 'test5.db' AS aux5; + ATTACH 'test6.db' AS aux6; + ATTACH 'test7.db' AS aux7; + ATTACH 'test8.db' AS aux8; } B step 200 B finish } {SQLITE_OK} +db2 close +forcedelete test2.db + +do_execsql_test 2.2 { + CREATE TABLE aux7.t3(a, b); + CREATE TABLE aux7.t4(a, b); + INSERT INTO t4 VALUES(1, 1); + INSERT INTO t4 VALUES(2, 2); + INSERT INTO t4 VALUES(3, 3); + CREATE INDEX aux7.i3 ON t3(a); + CREATE INDEX aux7.i4 ON t4(a); +} + +sqlite3 db2 test2.db + +do_test 2.3 { + sqlite3_backup B db2 main db aux7 + B step 1 + + execsql { + DETACH aux4; + } + + B step 200 + B finish +} {SQLITE_OK} + +do_test 2.4 { + sqlite3_backup B db2 main db aux7 + B finish +} {SQLITE_OK} finish_test -- 2.47.3