diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 4 | ||||
-rw-r--r-- | src/backup.c | 559 | ||||
-rw-r--r-- | src/btree.c | 227 | ||||
-rw-r--r-- | src/btree.h | 3 | ||||
-rw-r--r-- | src/btreeInt.h | 3 | ||||
-rw-r--r-- | src/build.c | 38 | ||||
-rw-r--r-- | src/main.c | 12 | ||||
-rw-r--r-- | src/pager.c | 32 | ||||
-rw-r--r-- | src/pager.h | 5 | ||||
-rw-r--r-- | src/sqlite.h.in | 174 | ||||
-rw-r--r-- | src/sqliteInt.h | 6 | ||||
-rw-r--r-- | src/tclsqlite.c | 4 | ||||
-rw-r--r-- | src/test1.c | 4 | ||||
-rw-r--r-- | src/test_backup.c | 148 | ||||
-rw-r--r-- | src/vacuum.c | 3 | ||||
-rw-r--r-- | src/vdbeaux.c | 4 |
16 files changed, 972 insertions, 254 deletions
diff --git a/src/attach.c b/src/attach.c index 2a7525ae9..bf2683fb3 100644 --- a/src/attach.c +++ b/src/attach.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.81 2008/12/10 16:45:51 drh Exp $ +** $Id: attach.c,v 1.82 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -265,7 +265,7 @@ static void detachFunc( "cannot DETACH database within transaction"); goto detach_error; } - if( sqlite3BtreeIsInReadTrans(pDb->pBt) ){ + if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){ sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); goto detach_error; } diff --git a/src/backup.c b/src/backup.c new file mode 100644 index 000000000..a03ac2ad5 --- /dev/null +++ b/src/backup.c @@ -0,0 +1,559 @@ +/* +** 2009 January 28 +** +** 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 contains the implementation of the sqlite3_backup_XXX() +** API functions and the related features. +** +** $Id: backup.c,v 1.1 2009/02/03 16:51:25 danielk1977 Exp $ +*/ +#include "sqliteInt.h" +#include "btreeInt.h" + +/* Macro to find the minimum of two numeric values. +*/ +#ifndef MIN +# define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +/* +** Structure allocated for each backup operation. +*/ +struct sqlite3_backup { + sqlite3* pDestDb; /* Destination database handle */ + 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 */ + Btree *pSrc; /* Source b-tree file */ + + int rc; /* Backup process error code */ + + /* These two variables are set by every call to backup_step(). They are + ** read by calls to backup_remaining() and backup_pagecount(). + */ + Pgno nRemaining; /* Number of pages left to copy */ + Pgno nPagecount; /* Total number of pages to copy */ + + sqlite3_backup *pNext; /* Next backup associated with source pager */ +}; + +/* +** THREAD SAFETY NOTES: +** +** Once it has been created using backup_init(), a single sqlite3_backup +** structure may be accessed via two groups of thread-safe entry points: +** +** * Via the sqlite3_backup_XXX() API function backup_step() and +** backup_finish(). Both these functions obtain the source database +** handle mutex and the mutex associated with the source BtShared +** structure, in that order. +** +** * Via the BackupUpdate() and BackupRestart() functions, which are +** invoked by the pager layer to report various state changes in +** the page cache associated with the source database. The mutex +** associated with the source database BtShared structure will always +** be held when either of these functions are invoked. +** +** The other sqlite3_backup_XXX() API functions, backup_remaining() and +** backup_pagecount() are not thread-safe functions. If they are called +** while some other thread is calling backup_step() or backup_finish(), +** the values returned may be invalid. There is no way for a call to +** BackupUpdate() or BackupRestart() to interfere with backup_remaining() +** or backup_pagecount(). +** +** Depending on the SQLite configuration, the database handles and/or +** the Btree objects may have their own mutexes that require locking. +** Non-sharable Btrees (in-memory databases for example), do not have +** associated mutexes. +*/ + +/* +** Return a pointer corresponding to database zDb (i.e. "main", "temp") +** in connection handle pDb. If such a database cannot be found, return +** 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 0 and write an +** error message to pErrorDb. +*/ +static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ + int i = sqlite3FindDbName(pDb, zDb); + + if( i==1 ){ + Parse sParse; + memset(&sParse, 0, sizeof(sParse)); + sParse.db = pDb; + if( sqlite3OpenTempDatabase(&sParse) ){ + sqlite3ErrorClear(&sParse); + sqlite3Error(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); + return 0; + } + assert( sParse.zErrMsg==0 ); + } + + if( i<0 ){ + sqlite3Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); + return 0; + } + + return pDb->aDb[i].pBt; +} + +/* +** Create an sqlite3_backup process to copy the contents of zSrcDb from +** connection handle pSrcDb to zDestDb in pDestDb. If successful, return +** a pointer to the new sqlite3_backup object. +** +** If an error occurs, NULL is returned and an error code and error message +** stored in database handle pDestDb. +*/ +sqlite3_backup *sqlite3_backup_init( + sqlite3* pDestDb, /* Database to write to */ + const char *zDestDb, /* Name of database within pDestDb */ + sqlite3* pSrcDb, /* Database connection to read from */ + const char *zSrcDb /* Name of database within pSrcDb */ +){ + sqlite3_backup *p; /* Value to return */ + + /* Lock the source database handle. The destination database + ** handle is not locked. The user is required to ensure that no + ** other thread accesses the destination handle for the duration + ** of the backup operation. + */ + sqlite3_mutex_enter(pSrcDb->mutex); + + if( pSrcDb==pDestDb ){ + sqlite3Error( + pDestDb, SQLITE_ERROR, "Source and destination handles must be distinct" + ); + p = 0; + }else { + /* Allocate space for a new sqlite3_backup object */ + p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup)); + if( !p ){ + sqlite3Error(pDestDb, SQLITE_NOMEM, 0); + } + } + + /* If the allocation succeeded, populate the new object. */ + if( p ){ + memset(p, 0, sizeof(sqlite3_backup)); + p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); + p->pDest = findBtree(pDestDb, pDestDb, zDestDb); + p->pDestDb = pDestDb; + p->pSrcDb = pSrcDb; + p->iNext = 1; + + if( 0==p->pSrc || 0==p->pDest ){ + /* One (or both) of the named databases did not exist. An error has + ** already been written into the pDestDb handle. All that is left + ** to do here is free the sqlite3_backup structure. + */ + sqlite3_free(p); + p = 0; + } + } + + /* If everything has gone as planned, attach the backup object to the + ** source pager. The source pager calls BackupUpdate() and BackupRestart() + ** to notify this module if the source file is modified mid-backup. + */ + if( p ){ + sqlite3_backup **pp; /* Pointer to head of pagers backup list */ + sqlite3BtreeEnter(p->pSrc); + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); + p->pNext = *pp; + *pp = p; + sqlite3BtreeLeave(p->pSrc); + p->pSrc->nBackup++; + } + + sqlite3_mutex_leave(pSrcDb->mutex); + return p; +} + +/* +** Parameter zSrcData points to a buffer containing the data for +** page iSrcPg from the source database. Copy this data into the +** destination database. +*/ +static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ + 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( p->bDestLocked ); + assert( p->rc==SQLITE_OK ); + assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); + assert( zSrcData ); + + /* Catch the case where the destination is an in-memory database and the + ** page sizes of the source and destination differ. + */ + if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(sqlite3BtreePager(p->pDest)) ){ + rc = SQLITE_READONLY; + } + + /* This loop runs once for each destination page spanned by the source + ** page. For each iteration, variable iOff is set to the byte offset + ** of the destination page. + */ + for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){ + DbPage *pDestPg = 0; + Pgno iDest = (Pgno)(iOff/nDestPgsz)+1; + if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue; + if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg)) + && SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg)) + ){ + const u8 *zIn = &zSrcData[iOff%nSrcPgsz]; + u8 *zDestData = sqlite3PagerGetData(pDestPg); + u8 *zOut = &zDestData[iOff%nDestPgsz]; + + /* Copy the data from the source page into the destination page. + ** Then clear the Btree layer MemPage.isInit flag. Both this module + ** and the pager code use this trick (clearing the first byte + ** of the page 'extra' space to invalidate the Btree layers + ** cached parse of the page). MemPage.isInit is marked + ** "MUST BE FIRST" for this purpose. + */ + memcpy(zOut, zIn, nCopy); + ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; + } + sqlite3PagerUnref(pDestPg); + } + + return rc; +} + +/* +** Copy nPage pages from the source b-tree to the destination. +*/ +int sqlite3_backup_step(sqlite3_backup *p, int nPage){ + int rc; + + sqlite3_mutex_enter(p->pSrcDb->mutex); + sqlite3BtreeEnter(p->pSrc); + + rc = p->rc; + if( rc==SQLITE_OK ){ + Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ + Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */ + int ii; /* Iterator variable */ + int nSrcPage; /* Size of source db in pages */ + int bCloseTrans = 0; /* True if src db requires unlocking */ + + /* If the source pager is currently in a write-transaction, return + ** SQLITE_LOCKED immediately. + */ + if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ + rc = SQLITE_LOCKED; + } + + /* Lock the destination database, if it is not locked already. */ + if( SQLITE_OK==rc && p->bDestLocked==0 + && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) + ){ + p->bDestLocked = 1; + rc = sqlite3BtreeGetMeta(p->pDest, 1, &p->iDestSchema); + } + + /* 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 && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ + rc = sqlite3BtreeBeginTrans(p->pSrc, 0); + bCloseTrans = 1; + } + + /* Now that there is a read-lock on the source database, query the + ** source pager for the number of pages in the database. + */ + if( rc==SQLITE_OK ){ + rc = sqlite3PagerPagecount(pSrcPager, &nSrcPage); + } + for(ii=0; ii<nPage && p->iNext<=nSrcPage && rc==SQLITE_OK; ii++){ + const Pgno iSrcPg = p->iNext; /* Source page number */ + if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ + DbPage *pSrcPg; /* Source page object */ + rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); + if( rc==SQLITE_OK ){ + rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); + sqlite3PagerUnref(pSrcPg); + } + } + p->iNext++; + } + if( rc==SQLITE_OK ){ + p->nPagecount = nSrcPage; + p->nRemaining = nSrcPage+1-p->iNext; + if( p->iNext>nSrcPage ){ + rc = SQLITE_DONE; + } + } + + if( rc==SQLITE_DONE ){ + const int nSrcPagesize = sqlite3BtreeGetPageSize(p->pSrc); + const int nDestPagesize = sqlite3BtreeGetPageSize(p->pDest); + int nDestTruncate; + sqlite3_file *pFile = 0; + i64 iSize; + + /* Update the schema version field in the destination database. This + ** is to make sure that the schema-version really does change in + ** the case where the source and destination databases have the + ** same schema version. + */ + sqlite3BtreeUpdateMeta(p->pDest, 1, p->iDestSchema+1); + + /* Set nDestTruncate to the final number of pages in the destination + ** database. The complication here is that the destination page + ** size may be different to the source page size. + ** + ** If the source page size is smaller than the destination page size, + ** round up. In this case the call to sqlite3OsTruncate() below will + ** fix the size of the file. However it is important to call + ** sqlite3PagerTruncateImage() here so that any pages in the + ** destination file that lie beyond the nDestTruncate page mark are + ** journalled by PagerCommitPhaseOne() before they are destroyed + ** by the file truncation. + */ + if( nSrcPagesize<nDestPagesize ){ + int ratio = nDestPagesize/nSrcPagesize; + nDestTruncate = (nSrcPage+ratio-1)/ratio; + }else{ + nDestTruncate = nSrcPage * (nSrcPagesize/nDestPagesize); + } + sqlite3PagerTruncateImage(pDestPager, nDestTruncate); + + if( nSrcPagesize<nDestPagesize ){ + /* If the source page-size is smaller than the destination page-size, + ** two extra things may need to happen: + ** + ** * The destination may need to be truncated, and + ** + ** * Data stored on the pages immediately following the + ** pending-byte page in the source database may need to be + ** copied into the destination database. + */ + iSize = (i64)nSrcPagesize * (i64)nSrcPage; + pFile = sqlite3PagerFile(pDestPager); + assert( pFile ); + if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1)) + && SQLITE_OK==(rc = sqlite3OsTruncate(pFile, iSize)) + && SQLITE_OK==(rc = sqlite3PagerSync(pDestPager)) + ){ + i64 iOff; + i64 iEnd = MIN(PENDING_BYTE + nDestPagesize, iSize); + for( + iOff=PENDING_BYTE+nSrcPagesize; + rc==SQLITE_OK && iOff<iEnd; + iOff+=nSrcPagesize + ){ + PgHdr *pSrcPg = 0; + const Pgno iSrcPg = (iOff/nSrcPagesize)+1; + rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); + if( rc==SQLITE_OK ){ + u8 *zData = sqlite3PagerGetData(pSrcPg); + rc = sqlite3OsWrite(pFile, zData, nSrcPagesize, iOff); + } + sqlite3PagerUnref(pSrcPg); + } + } + }else{ + rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); + } + + /* Finish committing the transaction to the destination database. */ + if( SQLITE_OK==rc + && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest)) + ){ + rc = SQLITE_DONE; + } + } + + /* If bCloseTrans is true, then this function opened a read transaction + ** on the source database. Close the read transaction here. There is + ** no need to check the return values of the btree methods here, as + ** "committing" a read-only transaction cannot fail. + */ + if( bCloseTrans ){ + TESTONLY( int rc2 ); + TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); + TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); + assert( rc2==SQLITE_OK ); + } + + if( rc!=SQLITE_LOCKED && rc!=SQLITE_BUSY ){ + p->rc = rc; + } + } + sqlite3BtreeLeave(p->pSrc); + sqlite3_mutex_leave(p->pSrcDb->mutex); + return rc; +} + +/* +** Release all resources associated with an sqlite3_backup* handle. +*/ +int sqlite3_backup_finish(sqlite3_backup *p){ + sqlite3_backup **pp; /* Ptr to head of pagers backup list */ + sqlite3_mutex *mutex; /* Mutex to protect source database */ + int rc; /* Value to return */ + + /* Enter the mutexes */ + sqlite3_mutex_enter(p->pSrcDb->mutex); + sqlite3BtreeEnter(p->pSrc); + mutex = p->pSrcDb->mutex; + + /* Detach this backup from the source pager. */ + if( p->pDestDb ){ + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); + while( *pp!=p ){ + pp = &(*pp)->pNext; + } + *pp = p->pNext; + p->pSrc->nBackup--; + } + + /* If a transaction is still open on the Btree, roll it back. */ + sqlite3BtreeRollback(p->pDest); + + /* Set the error code of the destination database handle. */ + rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; + sqlite3Error(p->pDestDb, rc, 0); + + /* Exit the mutexes and free the backup context structure. */ + sqlite3BtreeLeave(p->pSrc); + if( p->pDestDb ){ + sqlite3_free(p); + } + sqlite3_mutex_leave(mutex); + return rc; +} + +/* +** Return the number of pages still to be backed up as of the most recent +** call to sqlite3_backup_step(). +*/ +int sqlite3_backup_remaining(sqlite3_backup *p){ + return p->nRemaining; +} + +/* +** Return the total number of pages in the source database as of the most +** recent call to sqlite3_backup_step(). +*/ +int sqlite3_backup_pagecount(sqlite3_backup *p){ + return p->nPagecount; +} + +/* +** This function is called after the contents of page iPage of the +** source database have been modified. If page iPage has already been +** copied into the destination database, then the data written to the +** destination is now invalidated. The destination copy of iPage needs +** to be updated with the new data before the backup operation is +** complete. +** +** It is assumed that the mutex associated with the BtShared object +** corresponding to the source database is held when this function is +** called. +*/ +void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ + sqlite3_backup *p; /* Iterator variable */ + for(p=pBackup; p; p=p->pNext){ + assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); + if( p->rc==SQLITE_OK && iPage<p->iNext ){ + /* The backup process p has already copied page iPage. But now it + ** has been modified by a transaction on the source pager. Copy + ** the new data into the backup. + */ + int rc = backupOnePage(p, iPage, aData); + if( rc!=SQLITE_OK ){ + p->rc = rc; + } + } + } +} + +/* +** Restart the backup process. This is called when the pager layer +** detects that the database has been modified by an external database +** connection. In this case there is no way of knowing which of the +** pages that have been copied into the destination database are still +** valid and which are not, so the entire process needs to be restarted. +** +** It is assumed that the mutex associated with the BtShared object +** corresponding to the source database is held when this function is +** called. +*/ +void sqlite3BackupRestart(sqlite3_backup *pBackup){ + sqlite3_backup *p; /* Iterator variable */ + for(p=pBackup; p; p=p->pNext){ + assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); + p->iNext = 1; + } +} + +#ifndef SQLITE_OMIT_VACUUM +/* +** Copy the complete content of pBtFrom into pBtTo. A transaction +** must be active for both files. +** +** The size of file pTo may be reduced by this operation. If anything +** goes wrong, the transaction on pTo is rolled back. If successful, the +** transaction is committed before returning. +*/ +int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ + int rc; + sqlite3_backup b; + sqlite3BtreeEnter(pTo); + sqlite3BtreeEnter(pFrom); + + /* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set + ** to 0. This is used by the implementations of sqlite3_backup_step() + ** and sqlite3_backup_finish() to detect that they are being called + ** from this function, not directly by the user. + */ + memset(&b, 0, sizeof(b)); + 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 + ** file. By passing this as the number of pages to copy to + ** sqlite3_backup_step(), we can guarantee that the copy finishes + ** within a single call (unless an error occurs). The assert() statement + ** checks this assumption - (p->rc) should be set to either SQLITE_DONE + ** or an error code. + */ + sqlite3_backup_step(&b, 0x7FFFFFFF); + assert( b.rc!=SQLITE_OK ); + rc = sqlite3_backup_finish(&b); + if( rc==SQLITE_OK ){ + pTo->pBt->pageSizeFixed = 0; + } + + sqlite3BtreeLeave(pFrom); + sqlite3BtreeLeave(pTo); + return rc; +} +#endif /* SQLITE_OMIT_VACUUM */ + diff --git a/src/btree.c b/src/btree.c index d48f20880..b4a70bd97 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.563 2009/01/31 14:54:07 danielk1977 Exp $ +** $Id: btree.c,v 1.564 2009/02/03 16:51:25 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -7239,225 +7239,6 @@ const char *sqlite3BtreeGetJournalname(Btree *p){ return sqlite3PagerJournalname(p->pBt->pPager); } -#ifndef SQLITE_OMIT_VACUUM -/* -** Copy the complete content of pBtFrom into pBtTo. A transaction -** must be active for both files. -** -** The size of file pTo may be reduced by this operation. -** If anything goes wrong, the transaction on pTo is rolled back. -** -** If successful, CommitPhaseOne() may be called on pTo before returning. -** The caller should finish committing the transaction on pTo by calling -** sqlite3BtreeCommit(). -*/ -static int btreeCopyFile(Btree *pTo, Btree *pFrom){ - int rc = SQLITE_OK; - Pgno i; - - Pgno nFromPage; /* Number of pages in pFrom */ - Pgno nToPage; /* Number of pages in pTo */ - Pgno nNewPage; /* Number of pages in pTo after the copy */ - - Pgno iSkip; /* Pending byte page in pTo */ - int nToPageSize; /* Page size of pTo in bytes */ - int nFromPageSize; /* Page size of pFrom in bytes */ - - BtShared *pBtTo = pTo->pBt; - BtShared *pBtFrom = pFrom->pBt; - pBtTo->db = pTo->db; - pBtFrom->db = pFrom->db; - - nToPageSize = pBtTo->pageSize; - nFromPageSize = pBtFrom->pageSize; - - assert( pTo->inTrans==TRANS_WRITE ); - assert( pFrom->inTrans==TRANS_WRITE ); - if( NEVER(pBtTo->pCursor) ){ - return SQLITE_BUSY; - } - - nToPage = pagerPagecount(pBtTo); - nFromPage = pagerPagecount(pBtFrom); - iSkip = PENDING_BYTE_PAGE(pBtTo); - - /* Variable nNewPage is the number of pages required to store the - ** contents of pFrom using the current page-size of pTo. - */ - nNewPage = (Pgno) - (((i64)nFromPage*(i64)nFromPageSize+(i64)nToPageSize-1)/(i64)nToPageSize); - - for(i=1; rc==SQLITE_OK && (i<=nToPage || i<=nNewPage); i++){ - - /* Journal the original page. - ** - ** iSkip is the page number of the locking page (PENDING_BYTE_PAGE) - ** in database *pTo (before the copy). This page is never written - ** into the journal file. Unless i==iSkip or the page was not - ** present in pTo before the copy operation, journal page i from pTo. - */ - if( i!=iSkip && i<=nToPage ){ - DbPage *pDbPage = 0; - rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK && i>nFromPage ){ - /* Yeah. It seems wierd to call DontWrite() right after Write(). But - ** that is because the names of those procedures do not exactly - ** represent what they do. Write() really means "put this page in the - ** rollback journal and mark it as dirty so that it will be written - ** to the database file later." DontWrite() undoes the second part of - ** that and prevents the page from being written to the database. The - ** page is still on the rollback journal, though. And that is the - ** whole point of this block: to put pages on the rollback journal. - */ - sqlite3PagerDontWrite(pDbPage); - } - sqlite3PagerUnref(pDbPage); - } - } - - /* Overwrite the data in page i of the target database */ - if( rc==SQLITE_OK && i!=iSkip && i<=nNewPage ){ - - DbPage *pToPage = 0; - sqlite3_int64 iOff; - - rc = sqlite3PagerGet(pBtTo->pPager, i, &pToPage); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pToPage); - } - - for( - iOff=(i-1)*nToPageSize; - rc==SQLITE_OK && iOff<i*nToPageSize; - iOff += nFromPageSize - ){ - DbPage *pFromPage = 0; - Pgno iFrom = (Pgno)(iOff/nFromPageSize)+1; - - if( iFrom==PENDING_BYTE_PAGE(pBtFrom) ){ - continue; - } - - rc = sqlite3PagerGet(pBtFrom->pPager, iFrom, &pFromPage); - if( rc==SQLITE_OK ){ - char *zTo = sqlite3PagerGetData(pToPage); - char *zFrom = sqlite3PagerGetData(pFromPage); - int nCopy; - - if( nFromPageSize>=nToPageSize ){ - zFrom += ((i-1)*nToPageSize - ((iFrom-1)*nFromPageSize)); - nCopy = nToPageSize; - }else{ - zTo += (((iFrom-1)*nFromPageSize) - (i-1)*nToPageSize); - nCopy = nFromPageSize; - } - - memcpy(zTo, zFrom, nCopy); - sqlite3PagerUnref(pFromPage); - } - } - - if( pToPage ){ - MemPage *p = (MemPage *)sqlite3PagerGetExtra(pToPage); - p->isInit = 0; - sqlite3PagerUnref(pToPage); - } - } - } - - /* If things have worked so far, the database file may need to be - ** truncated. The complex part is that it may need to be truncated to - ** a size that is not an integer multiple of nToPageSize - the current - ** page size used by the pager associated with B-Tree pTo. - ** - ** For example, say the page-size of pTo is 2048 bytes and the original - ** number of pages is 5 (10 KB file). If pFrom has a page size of 1024 - ** bytes and 9 pages, then the file needs to be truncated to 9KB. - */ - if( rc==SQLITE_OK ){ - sqlite3_file *pFile = sqlite3PagerFile(pBtTo->pPager); - i64 iSize = (i64)nFromPageSize * (i64)nFromPage; - i64 iNow = (i64)((nToPage>nNewPage)?nToPage:nNewPage) * (i64)nToPageSize; - i64 iPending = ((i64)PENDING_BYTE_PAGE(pBtTo)-1) *(i64)nToPageSize; - - assert( iSize<=iNow ); - - /* Commit phase one syncs the journal file associated with pTo - ** containing the original data. It does not sync the database file - ** itself. After doing this it is safe to use OsTruncate() and other - ** file APIs on the database file directly. - */ - pBtTo->db = pTo->db; - if( nFromPageSize==nToPageSize ){ - sqlite3PagerTruncateImage(pBtTo->pPager, nFromPage); - iNow = iSize; - } - rc = sqlite3PagerCommitPhaseOne(pBtTo->pPager, 0, 1); - if( iSize<iNow && rc==SQLITE_OK ){ - rc = sqlite3OsTruncate(pFile, iSize); - } - - /* The loop that copied data from database pFrom to pTo did not - ** populate the locking page of database pTo. If the page-size of - ** pFrom is smaller than that of pTo, this means some data will - ** not have been copied. - ** - ** This block copies the missing data from database pFrom to pTo - ** using file APIs. This is safe because at this point we know that - ** all of the original data from pTo has been synced into the - ** journal file. At this point it would be safe to do anything at - ** all to the database file except truncate it to zero bytes. - */ - if( rc==SQLITE_OK && nFromPageSize<nToPageSize && iSize>iPending){ - i64 iOff; - for( - iOff=iPending; - rc==SQLITE_OK && iOff<(iPending+nToPageSize); - iOff += nFromPageSize - ){ - DbPage *pFromPage = 0; - Pgno iFrom = (Pgno)(iOff/nFromPageSize)+1; - - if( iFrom==PENDING_BYTE_PAGE(pBtFrom) || iFrom>nFromPage ){ - continue; - } - - rc = sqlite3PagerGet(pBtFrom->pPager, iFrom, &pFromPage); - if( rc==SQLITE_OK ){ - char *zFrom = sqlite3PagerGetData(pFromPage); - rc = sqlite3OsWrite(pFile, zFrom, nFromPageSize, iOff); - sqlite3PagerUnref(pFromPage); - } - } - } - } - - /* Sync the database file */ - if( rc==SQLITE_OK ){ - rc = sqlite3PagerSync(pBtTo->pPager); - } - if( rc==SQLITE_OK ){ - pBtTo->pageSizeFixed = 0; - }else{ - sqlite3BtreeRollback(pTo); - } - - return rc; -} -int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ - int rc; - sqlite3BtreeEnter(pTo); - sqlite3BtreeEnter(pFrom); - rc = btreeCopyFile(pTo, pFrom); - sqlite3BtreeLeave(pFrom); - sqlite3BtreeLeave(pTo); - return rc; -} - -#endif /* SQLITE_OMIT_VACUUM */ - /* ** Return non-zero if a transaction is active. */ @@ -7483,6 +7264,12 @@ int sqlite3BtreeIsInReadTrans(Btree *p){ return p->inTrans!=TRANS_NONE; } +int sqlite3BtreeIsInBackup(Btree *p){ + assert( p ); + assert( sqlite3_mutex_held(p->db->mutex) ); + return p->nBackup!=0; +} + /* ** This function returns a pointer to a blob of memory associated with ** a single shared-btree. The memory is used by client code for its own diff --git a/src/btree.h b/src/btree.h index 49744583b..dff9fe1be 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.107 2009/01/24 11:30:43 drh Exp $ +** @(#) $Id: btree.h,v 1.108 2009/02/03 16:51:25 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -98,6 +98,7 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInStmt(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); +int sqlite3BtreeIsInBackup(Btree*); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); int sqlite3BtreeSchemaLocked(Btree *); int sqlite3BtreeLockTable(Btree *, int, u8); diff --git a/src/btreeInt.h b/src/btreeInt.h index 2a97f9ee4..32aec9615 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btreeInt.h,v 1.41 2009/01/20 17:06:27 danielk1977 Exp $ +** $Id: btreeInt.h,v 1.42 2009/02/03 16:51:25 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -324,6 +324,7 @@ struct Btree { u8 sharable; /* True if we can share pBt with another db */ u8 locked; /* True if db currently has pBt locked */ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ + int nBackup; /* Number of backup operations reading this btree */ Btree *pNext; /* List of other sharable Btrees from the same db */ Btree *pPrev; /* Back pointer of the same list */ }; diff --git a/src/build.c b/src/build.c index 300546f0e..b54bfe06c 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.514 2009/02/03 15:50:34 drh Exp $ +** $Id: build.c,v 1.515 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -621,31 +621,41 @@ void sqlite3OpenMasterTable(Parse *p, int iDb){ } /* -** The token *pName contains the name of a database (either "main" or -** "temp" or the name of an attached db). This routine returns the -** index of the named database in db->aDb[], or -1 if the named db -** does not exist. +** Parameter zName points to a nul-terminated buffer containing the name +** of a database ("main", "temp" or the name of an attached db). This +** function returns the index of the named database in db->aDb[], or +** -1 if the named db cannot be found. */ -int sqlite3FindDb(sqlite3 *db, Token *pName){ - int i = -1; /* Database number */ - int n; /* Number of characters in the name */ - Db *pDb; /* A database whose name space is being searched */ - char *zName; /* Name we are searching for */ - - zName = sqlite3NameFromToken(db, pName); +int sqlite3FindDbName(sqlite3 *db, const char *zName){ + int i = -1; /* Database number */ if( zName ){ - n = sqlite3Strlen30(zName); + Db *pDb; + int n = sqlite3Strlen30(zName); for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){ if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) && 0==sqlite3StrICmp(pDb->zName, zName) ){ break; } } - sqlite3DbFree(db, zName); } return i; } +/* +** The token *pName contains the name of a database (either "main" or +** "temp" or the name of an attached db). This routine returns the +** index of the named database in db->aDb[], or -1 if the named db +** does not exist. +*/ +int sqlite3FindDb(sqlite3 *db, Token *pName){ + int i; /* Database number */ + char *zName; /* Name we are searching for */ + zName = sqlite3NameFromToken(db, pName); + i = sqlite3FindDbName(db, zName); + sqlite3DbFree(db, zName); + return i; +} + /* The table or view or trigger name is passed to this routine via tokens ** pName1 and pName2. If the table name was fully qualified, for example: ** diff --git a/src/main.c b/src/main.c index f104e41ec..58f5443ca 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.524 2009/02/03 15:50:34 drh Exp $ +** $Id: main.c,v 1.525 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -603,6 +603,16 @@ int sqlite3_close(sqlite3 *db){ } assert( sqlite3SafetyCheckSickOrOk(db) ); + for(j=0; j<db->nDb; j++){ + Btree *pBt = db->aDb[j].pBt; + if( pBt && sqlite3BtreeIsInBackup(pBt) ){ + sqlite3Error(db, SQLITE_BUSY, + "Unable to close due to unfinished backup operation"); + sqlite3_mutex_leave(db->mutex); + return SQLITE_BUSY; + } + } + /* Free any outstanding Savepoint structures. */ sqlite3CloseSavepoints(db); diff --git a/src/pager.c b/src/pager.c index 820e8bcab..10c09e749 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.561 2009/01/31 14:54:07 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.562 2009/02/03 16:51:25 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -306,6 +306,7 @@ struct Pager { char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ i64 journalSizeLimit; /* Size limit for persistent journal files */ PCache *pPCache; /* Pointer to page cache object */ + sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ }; /* @@ -1039,9 +1040,12 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ /* ** Unless the pager is in error-state, discard all in-memory pages. If ** the pager is in error-state, then this call is a no-op. +** +** TODO: Why can we not reset the pager while in error state? */ static void pager_reset(Pager *pPager){ if( SQLITE_OK==pPager->errCode ){ + sqlite3BackupRestart(pPager->pBackup); sqlite3PcacheClear(pPager->pPCache); } } @@ -1513,6 +1517,7 @@ static int pager_playback_one_page( if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } + sqlite3BackupUpdate(pPager->pBackup, pgno, aData); }else if( !isMainJrnl && pPg==0 ){ /* If this is a rollback of a savepoint and data was not written to ** the database and the page is not in-memory, there is a potential @@ -2875,6 +2880,9 @@ static int pager_write_pagelist(PgHdr *pList){ pPager->dbFileSize = pgno; } + /* Update any backup objects copying the contents of this pager. */ + sqlite3BackupUpdate(pPager->pBackup, pgno, (u8 *)pData); + PAGERTRACE(("STORE %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pList))); IOTRACE(("PGOUT %p %d\n", pPager, pgno)); @@ -3549,7 +3557,7 @@ static int pagerSharedLock(Pager *pPager){ ** playing back the hot-journal so that we don't end up with ** an inconsistent cache. */ - sqlite3PcacheClear(pPager->pPCache); + pager_reset(pPager); rc = pager_playback(pPager, 1); if( rc!=SQLITE_OK ){ rc = pager_error(pPager, rc); @@ -4436,7 +4444,9 @@ int sqlite3PagerCommitPhaseOne( /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is a no-op. */ - if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dbModified ){ + if( MEMDB && pPager->dbModified ){ + sqlite3BackupRestart(pPager->pBackup); + }else if( pPager->state!=PAGER_SYNCED && pPager->dbModified ){ /* The following block updates the change-counter. Exactly how it ** does this depends on whether or not the atomic-update optimization @@ -4747,10 +4757,14 @@ int *sqlite3PagerStats(Pager *pPager){ a[10] = pPager->nWrite; return a; } +#endif + +/* +** Return true if this is an in-memory pager. +*/ int sqlite3PagerIsMemdb(Pager *pPager){ return MEMDB; } -#endif /* ** Check that there are at least nSavepoint savepoints open. If there are @@ -5152,4 +5166,14 @@ i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ return pPager->journalSizeLimit; } +/* +** Return a pointer to the pPager->pBackup variable. The backup module +** in backup.c maintains the content of this variable. This module +** uses it opaquely as an argument to sqlite3BackupRestart() and +** sqlite3BackupUpdate() only. +*/ +sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ + return &pPager->pBackup; +} + #endif /* SQLITE_OMIT_DISKIO */ diff --git a/src/pager.h b/src/pager.h index 3d6e4bb85..011f91c40 100644 --- a/src/pager.h +++ b/src/pager.h @@ -13,7 +13,7 @@ ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.99 2009/01/31 14:54:07 danielk1977 Exp $ +** @(#) $Id: pager.h,v 1.100 2009/02/03 16:51:25 danielk1977 Exp $ */ #ifndef _PAGER_H_ @@ -100,6 +100,7 @@ void sqlite3PagerSetSafetyLevel(Pager*,int,int); int sqlite3PagerLockingMode(Pager *, int); int sqlite3PagerJournalMode(Pager *, int); i64 sqlite3PagerJournalSizeLimit(Pager *, i64); +sqlite3_backup **sqlite3PagerBackupPtr(Pager*); /* Functions used to obtain and release page references. */ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); @@ -135,6 +136,7 @@ sqlite3_file *sqlite3PagerFile(Pager*); const char *sqlite3PagerJournalname(Pager*); int sqlite3PagerNosync(Pager*); void *sqlite3PagerTempSpace(Pager*); +int sqlite3PagerIsMemdb(Pager*); /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); @@ -152,7 +154,6 @@ void sqlite3PagerTruncateImage(Pager*,Pgno); #ifdef SQLITE_TEST int *sqlite3PagerStats(Pager*); void sqlite3PagerRefdump(Pager*); - int sqlite3PagerIsMemdb(Pager*); void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 950b09f8d..0d23bf213 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.422 2009/01/23 16:45:01 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.423 2009/02/03 16:51:25 danielk1977 Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -6723,6 +6723,178 @@ struct sqlite3_pcache_methods { }; /* +** CAPI3REF: Online Backup API. +** EXPERIMENTAL +** +** This API is used to overwrite the contents of one database with that +** of another. It is useful either for creating backups of databases or +** for copying in-memory databases to or from persistent files. +** +** Exclusive access is required to the destination database for the +** duration of the operation. However the source database is only +** read-locked while it is actually being read, it is not locked +** continuously for the entire operation. Thus, the backup may be +** performed on a live database without preventing other users from +** writing to the database for an extended period of time. +** +** To perform a backup operation: +** <ol> +** <li>[sqlite3_backup_init()] is called once to initialize the backup, +** <li>[sqlite3_backup_step()] is called one or more times to transfer +** the data between the two databases, and finally +** <li>[sqlite3_backup_finish()] is called to release all resources +** associated with the backup operation. +** </ol> +** There should be exactly one call to sqlite3_backup_finish() for each +** successful call to sqlite3_backup_init(). +** +** <b>sqlite3_backup_init()</b> +** +** The first two arguments passed to [sqlite3_backup_init()] are the database +** handle associated with the destination database and the database name +** used to attach the destination database to the handle. The database name +** is "main" for the main database, "temp" for the temporary database, or +** the name specified as part of the ATTACH statement if the destination is +** an attached database. The third and fourth arguments passed to +** sqlite3_backup_init() identify the database handle and database name used +** to access the source database. The values passed for the source and +** destination database handle parameters must not be the same. +** +** If an error occurs within sqlite3_backup_init(), then NULL is returned +** and an error code and error message written into the database handle +** passed as the first argument. They may be retrieved using the +** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16() functions. +** Otherwise, if successful, an opaque handle of type sqlite3_backup* is +** returned. This handle may be used with the sqlite3_backup_step() and +** sqlite3_backup_finish() functions to perform the specified backup +** operation. +** +** <b>sqlite3_backup_step()</b> +** +** Function [sqlite3_backup_step()] is used to copy up to nPage pages between +** the source and destination databases, where nPage is the value of the +** second parameter passed to sqlite3_backup_step(). If nPage pages are +** succesfully copied, but there are still more pages to copy before the +** backup is complete, it returns SQLITE_OK. If no error occured and there +** are no more pages to copy, then SQLITE_DONE is returned. If an error +** occurs, then an SQLite error code is returned. As well as SQLITE_OK and +** SQLITE_DONE, a call to sqlite3_backup_step() may return SQLITE_READONLY, +** SQLITE_NOMEM, SQLITE_BUSY, SQLITE_LOCKED or an SQLITE_IOERR_XXX error code. +** +** As well as the case where the destination database file was opened for +** read-only access, sqlite3_backup_step() may return SQLITE_READONLY if +** the destination is an in-memory database with a different page size +** from the source database. +** +** If sqlite3_backup_step() cannot obtain a required file-system lock, then +** the busy-handler function is invoked (if one is specified). If the +** busy-handler returns non-zero before the lock is available, then +** SQLITE_BUSY is returned to the caller. In this case the call to +** sqlite3_backup_step() can be retried later. If the source database handle +** is being used to write to the source database when sqlite3_backup_step() +** is called, then SQLITE_LOCKED is returned immediately. Again, in this +** case the call to sqlite3_backup_step() can be retried later on. If +** SQLITE_IOERR_XXX, SQLITE_NOMEM or SQLITE_READONLY is returned, then +** there is no point in retrying the call to sqlite3_backup_step(). These +** errors are considered fatal. At this point the application must accept +** that the backup operation has failed and pass the backup operation handle +** to the sqlite3_backup_finish() to release associated resources. +** +** Following the first call to sqlite3_backup_step(), an exclusive lock is +** obtained on the destination file. It is not released until either +** sqlite3_backup_finish() is called or the backup operation is complete +** and sqlite3_backup_step() returns SQLITE_DONE. Additionally, each time +** a call to sqlite3_backup_step() is made a shared lock is obtained on +** the source database file. This lock is released before the +** sqlite3_backup_step() call returns. Because the source database is not +** locked between calls to sqlite3_backup_step(), it may be modified mid-way +** through the backup procedure. If the source database is modified by an +** external process or via a database connection other than the one being +** used by the backup operation, then the backup will be transparently +** restarted by the next call to sqlite3_backup_step(). If the source +** database is modified by the using the same database connection as is used +** by the backup operation, then the backup database is transparently +** updated at the same time. +** +** <b>sqlite3_backup_finish()</b> +** +** Once sqlite3_backup_step() has returned SQLITE_DONE, or when the +** application wishes to abandon the backup operation, the sqlite3_backup* +** handle should be passed to sqlite3_backup_finish(). This releases all +** resources associated with the backup operation. If sqlite3_backup_step() +** has not yet returned SQLITE_DONE, then any active write-transaction on the +** destination database is rolled back. The sqlite3_backup* handle is invalid +** and may not be used following a call to sqlite3_backup_finish(). +** +** The value returned by sqlite3_backup_finish is SQLITE_OK if no error +** occured, regardless or whether or not sqlite3_backup_step() was called +** a sufficient number of times to complete the backup operation. Or, if +** an out-of-memory condition or IO error occured during a call to +** sqlite3_backup_step() then SQLITE_NOMEM or an SQLITE_IOERR_XXX error code +** is returned. In this case the error code and an error message are +** written to the destination database handle. +** +** A return of SQLITE_BUSY or SQLITE_LOCKED from sqlite3_backup_step() is +** not considered an error and does not affect the return value of +** sqlite3_backup_finish(). +** +** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> +** +** Each call to sqlite3_backup_step() sets two values stored internally +** by an sqlite3_backup* handle. The number of pages still to be backed +** up, which may be queried by sqlite3_backup_remaining(), and the total +** number of pages in the source database file, which may be queried by +** sqlite3_backup_pagecount(). +** +** The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified during a backup +** operation, then the values are not updated to account for any extra +** pages that need to be updated or the size of the source database file +** changing. +** +** <b>Concurrent Usage of Database Handles</b> +** +** The source database handle may be used by the application for other +** purposes while a backup operation is underway or being initialized. +** If SQLite is compiled and configured to support threadsafe database +** connections, then the source database connection may be used concurrently +** from within other threads. +** +** However, the application must guarantee that the destination database +** connection handle is not passed to any other API (by any thread) after +** sqlite3_backup_init() is called and before the corresponding call to +** sqlite3_backup_finish(). Unfortunately SQLite does not currently check +** for this, if the application does use the destination database handle +** for some other purpose during a backup operation, things may appear to +** work correctly but in fact be subtly malfunctioning. +** +** Furthermore, if running in shared cache mode, the application must +** guarantee that the shared cache used by the destination database +** is not accessed while the backup is running. In practice this means +** that the application must guarantee that the file-system file being +** backed up to is not accessed by any connection within the process, +** not just the specific connection that was passed to sqlite3_backup_init(). +** +** The sqlite3_backup handle itself is partially threadsafe. Multiple +** threads may safely make multiple concurrent calls to sqlite3_backup_step(). +** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** APIs are not strictly speaking threadsafe. If they are invoked at the +** same time as another thread is invoking sqlite3_backup_step() it is +** possible that they return invalid values. +*/ +typedef struct sqlite3_backup sqlite3_backup; +sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ + const char *zDestName, /* Destination database name */ + sqlite3 *pSource, /* Source database handle */ + const char *zSourceName /* Source database name */ +); +int sqlite3_backup_step(sqlite3_backup *p, int nPage); +int sqlite3_backup_finish(sqlite3_backup *p); +int sqlite3_backup_remaining(sqlite3_backup *p); +int sqlite3_backup_pagecount(sqlite3_backup *p); + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 287d2c95f..8db2e69e3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.830 2009/01/24 11:30:43 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.831 2009/02/03 16:51:25 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -2552,6 +2552,7 @@ char sqlite3AffinityType(const Token*); void sqlite3Analyze(Parse*, Token*, Token*); int sqlite3InvokeBusyHandler(BusyHandler*); int sqlite3FindDb(sqlite3*, Token*); +int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB); void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); @@ -2573,6 +2574,9 @@ char *sqlite3StrAccumFinish(StrAccum*); void sqlite3StrAccumReset(StrAccum*); void sqlite3SelectDestInit(SelectDest*,int,int); +void sqlite3BackupRestart(sqlite3_backup *); +void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); + /* ** The interface to the LEMON-generated parser */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 0e35f41e4..1516728d5 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -12,7 +12,7 @@ ** A TCL Interface to SQLite. Append this file to sqlite3.c and ** compile the whole thing to build a TCL-enabled version of SQLite. ** -** $Id: tclsqlite.c,v 1.234 2009/01/14 23:38:03 drh Exp $ +** $Id: tclsqlite.c,v 1.235 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "tcl.h" #include <errno.h> @@ -2663,6 +2663,7 @@ int TCLSH_MAIN(int argc, char **argv){ extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); + extern int Sqlitetestbackup_Init(Tcl_Interp*); Md5_Init(interp); Sqliteconfig_Init(interp); @@ -2686,6 +2687,7 @@ int TCLSH_MAIN(int argc, char **argv){ SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); + Sqlitetestbackup_Init(interp); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); diff --git a/src/test1.c b/src/test1.c index eff89a7de..326c67daf 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.346 2009/02/03 16:25:48 drh Exp $ +** $Id: test1.c,v 1.347 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -3217,7 +3217,7 @@ static int test_errcode( } /* -** Usage: test_errmsg DB +** Usage: sqlite3_errmsg DB ** ** Returns the UTF-8 representation of the error message string for the ** most recent sqlite3_* API call. diff --git a/src/test_backup.c b/src/test_backup.c new file mode 100644 index 000000000..1a1418d1c --- /dev/null +++ b/src/test_backup.c @@ -0,0 +1,148 @@ +/* +** 2009 January 28 +** +** 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. +** +************************************************************************* +** +** $Id: test_backup.c,v 1.1 2009/02/03 16:51:25 danielk1977 Exp $ +*/ + +#include "tcl.h" +#include <sqlite3.h> +#include <assert.h> + +/* These functions are implemented in test1.c. */ +int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); +const char *sqlite3TestErrorName(int); + +static int backupTestCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + enum BackupSubCommandEnum { + BACKUP_STEP, BACKUP_FINISH, BACKUP_REMAINING, BACKUP_PAGECOUNT + }; + struct BackupSubCommand { + const char *zCmd; + enum BackupSubCommandEnum eCmd; + int nArg; + const char *zArg; + } aSub[] = { + {"step", BACKUP_STEP , 1, "npage" }, + {"finish", BACKUP_FINISH , 0, "" }, + {"remaining", BACKUP_REMAINING , 0, "" }, + {"pagecount", BACKUP_PAGECOUNT , 0, "" }, + {0, 0, 0, 0} + }; + + sqlite3_backup *p = (sqlite3_backup *)clientData; + int iCmd; + int rc; + + rc = Tcl_GetIndexFromObjStruct( + interp, objv[1], aSub, sizeof(aSub[0]), "option", 0, &iCmd + ); + if( rc!=TCL_OK ){ + return rc; + } + if( objc!=(2 + aSub[iCmd].nArg) ){ + Tcl_WrongNumArgs(interp, 2, objv, aSub[iCmd].zArg); + return TCL_ERROR; + } + + switch( aSub[iCmd].eCmd ){ + + case BACKUP_FINISH: { + Tcl_CmdInfo cmdInfo; + Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objv[0]); + Tcl_GetCommandInfoFromToken(cmd, &cmdInfo); + cmdInfo.deleteProc = 0; + Tcl_SetCommandInfoFromToken(cmd, &cmdInfo); + Tcl_DeleteCommandFromToken(interp, cmd); + + rc = sqlite3_backup_finish(p); + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + break; + } + + case BACKUP_STEP: { + int nPage; + if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &nPage) ){ + return TCL_ERROR; + } + rc = sqlite3_backup_step(p, nPage); + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + break; + } + + case BACKUP_REMAINING: + Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_backup_remaining(p))); + break; + + case BACKUP_PAGECOUNT: + Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_backup_pagecount(p))); + break; + } + + return TCL_OK; +} + +static void backupTestFinish(ClientData clientData){ + sqlite3_backup *pBackup = (sqlite3_backup *)clientData; + sqlite3_backup_finish(pBackup); +} + +/* +** sqlite3_backup CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME +** +*/ +static int backupTestInit( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + sqlite3_backup *pBackup; + sqlite3 *pDestDb; + sqlite3 *pSrcDb; + const char *zDestName; + const char *zSrcName; + const char *zCmd; + + if( objc!=6 ){ + Tcl_WrongNumArgs( + interp, 1, objv, "CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME" + ); + return TCL_ERROR; + } + + zCmd = Tcl_GetString(objv[1]); + getDbPointer(interp, Tcl_GetString(objv[2]), &pDestDb); + zDestName = Tcl_GetString(objv[3]); + getDbPointer(interp, Tcl_GetString(objv[4]), &pSrcDb); + zSrcName = Tcl_GetString(objv[5]); + + pBackup = sqlite3_backup_init(pDestDb, zDestName, pSrcDb, zSrcName); + if( !pBackup ){ + Tcl_AppendResult(interp, "sqlite3_backup_init() failed", 0); + return TCL_ERROR; + } + + Tcl_CreateObjCommand(interp, zCmd, backupTestCmd, pBackup, backupTestFinish); + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; +} + +int Sqlitetestbackup_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "sqlite3_backup", backupTestInit, 0, 0); + return TCL_OK; +} + diff --git a/src/vacuum.c b/src/vacuum.c index 2a16df43e..c6227303d 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,7 +14,7 @@ ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.85 2009/01/22 23:04:46 drh Exp $ +** $Id: vacuum.c,v 1.86 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -265,7 +265,6 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp)); #endif - rc = sqlite3BtreeCommit(pMain); } if( rc==SQLITE_OK ){ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 54528ea38..9bc5beff4 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -14,7 +14,7 @@ ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. ** -** $Id: vdbeaux.c,v 1.434 2009/01/20 17:06:27 danielk1977 Exp $ +** $Id: vdbeaux.c,v 1.435 2009/02/03 16:51:25 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -1298,7 +1298,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){ - for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ + for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = sqlite3BtreeCommitPhaseOne(pBt, 0); |