/* ** If pFile is currently larger than iSize bytes, then truncate it to ** exactly iSize bytes. If pFile is not larger than iSize bytes, then ** this function is a no-op. ** ** Return SQLITE_OK if everything is successful, or an SQLite error ** code if an error occurs. */ static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){ i64 iCurrent; int rc = sqlite3OsFileSize(pFile, &iCurrent); if( rc==SQLITE_OK && iCurrent>iSize ){ rc = sqlite3OsTruncate(pFile, iSize); } return rc; }
/* ** Query the size of the file in bytes. */ static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ int rc = SQLITE_OK; JournalFile *p = (JournalFile *)pJfd; if( p->pReal ){ rc = sqlite3OsFileSize(p->pReal, pSize); }else{ *pSize = (sqlite_int64) p->iSize; } return rc; }
/* ** Open a crash-file file handle. ** ** The caller will have allocated pVfs->szOsFile bytes of space ** at pFile. This file uses this space for the CrashFile structure ** and allocates space for the "real" file structure using ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is ** equal or greater than sizeof(CrashFile). */ static int cfOpen( sqlite3_vfs *pCfVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; int rc; CrashFile *pWrapper = (CrashFile *)pFile; sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1]; memset(pWrapper, 0, sizeof(CrashFile)); rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags); if( rc==SQLITE_OK ){ i64 iSize; pWrapper->pMethod = &CrashFileVtab; pWrapper->zName = (char *)zName; pWrapper->pRealFile = pReal; rc = sqlite3OsFileSize(pReal, &iSize); pWrapper->iSize = (int)iSize; pWrapper->flags = flags; } if( rc==SQLITE_OK ){ pWrapper->nData = (4096 + pWrapper->iSize); pWrapper->zData = crash_malloc(pWrapper->nData); if( pWrapper->zData ){ /* os_unix.c contains an assert() that fails if the caller attempts ** to read data from the 512-byte locking region of a file opened ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file ** never contains valid data anyhow. So avoid doing such a read here. */ const int isDb = (flags&SQLITE_OPEN_MAIN_DB); i64 iChunk = pWrapper->iSize; if( iChunk>PENDING_BYTE && isDb ){ iChunk = PENDING_BYTE; } memset(pWrapper->zData, 0, pWrapper->nData); rc = sqlite3OsRead(pReal, pWrapper->zData, iChunk, 0); if( SQLITE_OK==rc && pWrapper->iSize>(PENDING_BYTE+512) && isDb ){ i64 iOff = PENDING_BYTE+512; iChunk = pWrapper->iSize - iOff; rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], iChunk, iOff); } }else{ rc = SQLITE_NOMEM; } } if( rc!=SQLITE_OK && pWrapper->pMethod ){ sqlite3OsClose(pFile); } return rc; }
/* ** Open a crash-file file handle. ** ** The caller will have allocated pVfs->szOsFile bytes of space ** at pFile. This file uses this space for the CrashFile structure ** and allocates space for the "real" file structure using ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is ** equal or greater than sizeof(CrashFile). */ static int cfOpen( sqlite3_vfs *pCfVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; int rc; CrashFile *pWrapper = (CrashFile *)pFile; sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1]; memset(pWrapper, 0, sizeof(CrashFile)); rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags); if( rc==SQLITE_OK ){ i64 iSize; pWrapper->pMethod = &CrashFileVtab; pWrapper->zName = (char *)zName; pWrapper->pRealFile = pReal; rc = sqlite3OsFileSize(pReal, &iSize); pWrapper->iSize = (int)iSize; pWrapper->flags = flags; } if( rc==SQLITE_OK ){ pWrapper->nData = (int)(4096 + pWrapper->iSize); pWrapper->zData = crash_malloc(pWrapper->nData); if( pWrapper->zData ){ /* os_unix.c contains an assert() that fails if the caller attempts ** to read data from the 512-byte locking region of a file opened ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file ** never contains valid data anyhow. So avoid doing such a read here. ** ** UPDATE: It also contains an assert() verifying that each call ** to the xRead() method reads less than 128KB of data. */ i64 iOff; memset(pWrapper->zData, 0, pWrapper->nData); for(iOff=0; iOff<pWrapper->iSize; iOff += 512){ int nRead = (int)(pWrapper->iSize - iOff); if( nRead>512 ) nRead = 512; rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff); } }else{ rc = SQLITE_NOMEM; } } if( rc!=SQLITE_OK && pWrapper->pMethod ){ sqlite3OsClose(pFile); } return rc; }
/* ** Open a crash-file file handle. ** ** The caller will have allocated pVfs->szOsFile bytes of space ** at pFile. This file uses this space for the CrashFile structure ** and allocates space for the "real" file structure using ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is ** equal or greater than sizeof(CrashFile). */ static int cfOpen( sqlite3_vfs *pCfVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; int rc; CrashFile *pWrapper = (CrashFile *)pFile; sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1]; memset(pWrapper, 0, sizeof(CrashFile)); rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags); if( rc==SQLITE_OK ){ i64 iSize; pWrapper->pMethod = &CrashFileVtab; pWrapper->zName = (char *)zName; pWrapper->pRealFile = pReal; rc = sqlite3OsFileSize(pReal, &iSize); pWrapper->iSize = (int)iSize; } if( rc==SQLITE_OK ){ pWrapper->nData = (4096 + pWrapper->iSize); pWrapper->zData = sqlite3_malloc(pWrapper->nData); if( pWrapper->zData ){ memset(pWrapper->zData, 0, pWrapper->nData); rc = sqlite3OsRead(pReal, pWrapper->zData, pWrapper->iSize, 0); }else{ rc = SQLITE_NOMEM; } } if( rc!=SQLITE_OK && pWrapper->pMethod ){ sqlite3OsClose(pFile); } return rc; }
/* ** Read the size of the file. First we read the size of the file system ** entry, then adjust for any ASYNC_WRITE or ASYNC_TRUNCATE operations ** currently in the write-op list. ** ** This method holds the mutex from start to finish. */ int asyncFileSize(OsFile *id, i64 *pSize){ int rc = SQLITE_OK; i64 s = 0; OsFile *pBase; pthread_mutex_lock(&async.queueMutex); /* Read the filesystem size from the base file. If pBaseRead is NULL, this ** means the file hasn't been opened yet. In this case all relevant data ** must be in the write-op queue anyway, so we can omit reading from the ** file-system. */ pBase = ((AsyncFile *)id)->pBaseRead; if( pBase ){ rc = sqlite3OsFileSize(pBase, &s); } if( rc==SQLITE_OK ){ AsyncWrite *p; for(p=async.pQueueFirst; p; p = p->pNext){ if( p->pFile==(AsyncFile *)id ){ switch( p->op ){ case ASYNC_WRITE: s = MAX(p->iOffset + (i64)(p->nByte), s); break; case ASYNC_TRUNCATE: s = MIN(s, p->iOffset); break; } } } *pSize = s; } pthread_mutex_unlock(&async.queueMutex); return rc; }
/* ** Flush the write-list as if xSync() had been called on file handle ** pFile. If isCrash is true, simulate a crash. */ static int writeListSync(CrashFile *pFile, int isCrash){ int rc = SQLITE_OK; int iDc = g.iDeviceCharacteristics; WriteBuffer *pWrite; WriteBuffer **ppPtr; /* If this is not a crash simulation, set pFinal to point to the ** last element of the write-list that is associated with file handle ** pFile. ** ** If this is a crash simulation, set pFinal to an arbitrarily selected ** element of the write-list. */ WriteBuffer *pFinal = 0; if( !isCrash ){ for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){ if( pWrite->pFile==pFile ){ pFinal = pWrite; } } }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){ int nWrite = 0; int iFinal; for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++; sqlite3Randomness(sizeof(int), &iFinal); iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite; for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--; pFinal = pWrite; } #ifdef TRACE_CRASHTEST printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a")); #endif ppPtr = &g.pWriteList; for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){ sqlite3_file *pRealFile = pWrite->pFile->pRealFile; /* (eAction==1) -> write block out normally, ** (eAction==2) -> do nothing, ** (eAction==3) -> trash sectors. */ int eAction = 0; if( !isCrash ){ eAction = 2; if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){ eAction = 1; } }else{ char random; sqlite3Randomness(1, &random); /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag ** is set or this is an OsTruncate(), not an Oswrite(). */ if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){ random &= 0x01; } /* If IOCAP_SEQUENTIAL is set and this is not the final entry ** in the truncated write-list, always select option 1 (write ** out correctly). */ if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){ random = 0; } /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is ** an append (first byte of the written region is 1 byte past the ** current EOF), always select option 1 (write out correctly). */ if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){ i64 iSize; sqlite3OsFileSize(pRealFile, &iSize); if( iSize==pWrite->iOffset ){ random = 0; } } if( (random&0x06)==0x06 ){ eAction = 3; }else{ eAction = ((random&0x01)?2:1); } } switch( eAction ){ case 1: { /* Write out correctly */ if( pWrite->zBuf ){ rc = sqlite3OsWrite( pRealFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset ); }else{ rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset); } *ppPtr = pWrite->pNext; #ifdef TRACE_CRASHTEST if( isCrash ){ printf("Writing %d bytes @ %d (%s)\n", pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName ); } #endif sqlite3_free(pWrite); break; } case 2: { /* Do nothing */ ppPtr = &pWrite->pNext; #ifdef TRACE_CRASHTEST if( isCrash ){ printf("Omiting %d bytes @ %d (%s)\n", pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName ); } #endif break; } case 3: { /* Trash sectors */ u8 *zGarbage; int iFirst = (pWrite->iOffset/g.iSectorSize); int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize; assert(pWrite->zBuf); #ifdef TRACE_CRASHTEST printf("Trashing %d sectors @ sector %d (%s)\n", 1+iLast-iFirst, iFirst, pWrite->pFile->zName ); #endif zGarbage = sqlite3_malloc(g.iSectorSize); if( zGarbage ){ sqlite3_int64 i; for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ sqlite3Randomness(g.iSectorSize, zGarbage); rc = sqlite3OsWrite( pRealFile, zGarbage, g.iSectorSize, i*g.iSectorSize ); } sqlite3_free(zGarbage); }else{ rc = SQLITE_NOMEM; } ppPtr = &pWrite->pNext; break; } default: assert(!"Cannot happen"); } if( pWrite==pFinal ) break; } if( rc==SQLITE_OK && isCrash ){ exit(-1); } for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext); g.pWriteListEnd = pWrite; return rc; }
/* ** Return the current file-size of an devsym-file. */ static int devsymFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsFileSize(p->pReal, pSize); }
/* ** Return the current file-size of an jt-file. */ static int jtFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ jt_file *p = (jt_file *)pFile; return sqlite3OsFileSize(p->pReal, pSize); }
//Read/Write/Seek/Truncate test void Test2() { sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ); TEST(vfs != NULL); sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile); TEST(osFile != NULL); //Creating a new file int err = sqlite3OsDelete(vfs, KTestFile1Z, 0); TEST2(err, SQLITE_OK); int res = 0; err = sqlite3OsAccess(vfs, KTestFile1Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); TEST2(err, SQLITE_OK); //Writing at the beginning of the file err = sqlite3OsWrite(osFile, "123456", 6, 0); TEST2(err, SQLITE_OK); //Verify the written data char data[20]; err = sqlite3OsRead(osFile, data, 6, 0); TEST2(err, SQLITE_OK); err = memcmp(data, "123456", 6); TEST2(err, 0); //Writing at beyond the end of the file err = sqlite3OsWrite(osFile, "abcdefgh", 8, 100); TEST2(err, SQLITE_OK); //Verify the written data err = sqlite3OsRead(osFile, data, 8, 100); TEST2(err, SQLITE_OK); err = memcmp(data, "abcdefgh", 8); TEST2(err, 0); //Truncate the file err = sqlite3OsTruncate(osFile, 3); TEST2(err, SQLITE_OK); //Write more data err = sqlite3OsWrite(osFile, "xyz", 3, 3); TEST2(err, SQLITE_OK); //Verify the written data err = sqlite3OsRead(osFile, data, 6, 0); TEST2(err, SQLITE_OK); err = memcmp(data, "123xyz", 6); TEST2(err, 0); //Check the file size TInt64 fileSize = 0; err = sqlite3OsFileSize(osFile, &fileSize); TEST2(err, SQLITE_OK); TEST(fileSize == 6); //FileControl - lock type int lockType = -1; err = osFile->pMethods->xFileControl(osFile, SQLITE_FCNTL_LOCKSTATE, &lockType); TEST2(err, SQLITE_OK); TEST2(lockType, NO_LOCK); //FileControl - set callback - NULL callback err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, 0); TEST2(err, SQLITE_ERROR); //FileControl - set callback - invalid callback object TSqlFreePageCallback cbck; err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, &cbck); TEST2(err, SQLITE_ERROR); //FileControl - invalid op-code err = osFile->pMethods->xFileControl(osFile, 90234, 0); TEST2(err, SQLITE_ERROR); //Close the file err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); // err = sqlite3OsDelete(vfs, KTestFile1Z, 0); TEST2(err, SQLITE_OK); User::Free(osFile); }
/* ** Return the current file-size of an tvfs-file. */ static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsFileSize(p->pReal, pSize); }
/* ** Read data from the file. First we read from the filesystem, then adjust ** the contents of the buffer based on ASYNC_WRITE operations in the ** write-op queue. ** ** This method holds the mutex from start to finish. */ static int asyncRead(OsFile *id, void *obuf, int amt){ int rc = SQLITE_OK; i64 filesize; int nRead; AsyncFile *pFile = (AsyncFile *)id; OsFile *pBase = pFile->pBaseRead; /* If an I/O error has previously occurred on this file, then all ** subsequent operations fail. */ if( async.ioError!=SQLITE_OK ){ return async.ioError; } /* Grab the write queue mutex for the duration of the call */ pthread_mutex_lock(&async.queueMutex); if( pBase ){ rc = sqlite3OsFileSize(pBase, &filesize); if( rc!=SQLITE_OK ){ goto asyncread_out; } rc = sqlite3OsSeek(pBase, pFile->iOffset); if( rc!=SQLITE_OK ){ goto asyncread_out; } nRead = MIN(filesize - pFile->iOffset, amt); if( nRead>0 ){ rc = sqlite3OsRead(pBase, obuf, nRead); ASYNC_TRACE(("READ %s %d bytes at %d\n", pFile->zName, nRead, pFile->iOffset)); } } if( rc==SQLITE_OK ){ AsyncWrite *p; i64 iOffset = pFile->iOffset; /* Current seek offset */ for(p=async.pQueueFirst; p; p = p->pNext){ if( p->pFile==pFile && p->op==ASYNC_WRITE ){ int iBeginOut = (p->iOffset - iOffset); int iBeginIn = -iBeginOut; int nCopy; if( iBeginIn<0 ) iBeginIn = 0; if( iBeginOut<0 ) iBeginOut = 0; nCopy = MIN(p->nByte-iBeginIn, amt-iBeginOut); if( nCopy>0 ){ memcpy(&((char *)obuf)[iBeginOut], &p->zBuf[iBeginIn], nCopy); ASYNC_TRACE(("OVERREAD %d bytes at %d\n", nCopy, iBeginOut+iOffset)); } } } pFile->iOffset += (i64)amt; } asyncread_out: pthread_mutex_unlock(&async.queueMutex); return rc; }
/* ** This routine implements the OP_Vacuum opcode of the VDBE. */ int sqlite3RunVacuum( char **pzErrMsg, /* Write error message here */ sqlite3 *db, /* Database connection */ int iDb, /* Which attached DB to vacuum */ sqlite3_value *pOut /* Write results here, if not NULL */ ){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ u64 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); return SQLITE_ERROR; } if( pOut ){ if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){ sqlite3SetString(pzErrMsg, db, "non-text filename"); return SQLITE_ERROR; } zOut = (const char*)sqlite3_value_text(pOut); }else{ zOut = ""; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_mDbFlags = db->mDbFlags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimisation would be to use a non-journaled pager. ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially ** empty. Only the journal header is written. Apparently it takes more ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ nDb = db->nDb; rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); pTemp = pDb->pBt; if( pOut ){ sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); i64 sz = 0; if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ rc = SQLITE_ERROR; sqlite3SetString(pzErrMsg, db, "output file already exists"); goto end_of_vacuum; } } nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; sqlite3CodecGetKey(db, iDb, (void**)&zKey, &nKey); if( nKey ) db->nextPagesize = 0; } #endif sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); /* Begin a transaction and take an exclusive lock on the main database ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, ** to ensure that we do not try to change the page-size on a WAL database. */ rc = execSql(db, pzErrMsg, "BEGIN"); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Do not attempt to change the page size for a WAL database */ if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) ==PAGER_JOURNALMODE_WAL ){ db->nextPagesize = 0; } if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0) || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) || NEVER(db->mallocFailed) ){ rc = SQLITE_NOMEM_BKPT; goto end_of_vacuum; } #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac : sqlite3BtreeGetAutoVacuum(pMain)); #endif /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */ rc = execSqlF(db, pzErrMsg, "SELECT sql FROM \"%w\".sqlite_master" " WHERE type='table'AND name<>'sqlite_sequence'" " AND coalesce(rootpage,1)>0", zDbMain ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execSqlF(db, pzErrMsg, "SELECT sql FROM \"%w\".sqlite_master" " WHERE type='index'", zDbMain ); if( rc!=SQLITE_OK ) goto end_of_vacuum; db->init.iDb = 0; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO vacuum_db.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" "FROM vacuum_db.sqlite_master " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbMain ); assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); db->mDbFlags &= ~DBFLAG_Vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy the triggers, views, and virtual tables from the main database ** over to the temporary database. None of these objects has any ** associated storage, so all we have to do is copy their entries ** from the SQLITE_MASTER table. */ rc = execSqlF(db, pzErrMsg, "INSERT INTO vacuum_db.sqlite_master" " SELECT*FROM \"%w\".sqlite_master" " WHERE type IN('view','trigger')" " OR(type='table'AND rootpage=0)", zDbMain ); if( rc ) goto end_of_vacuum; /* At this point, there is a write transaction open on both the ** vacuum database and the main database. Assuming no error occurs, ** both transactions are closed by this block - the main database ** transaction by sqlite3BtreeCopyFile() and the other by an explicit ** call to sqlite3BtreeCommit(). */ { u32 meta; int i; /* This array determines which meta meta values are preserved in the ** vacuum. Even entries are the meta value number and odd entries ** are an increment to apply to the meta value after the vacuum. ** The increment is used to increase the schema cookie so that other ** connections to the same database will know to reread the schema. */ static const unsigned char aCopy[] = { BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */ BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ BTREE_USER_VERSION, 0, /* Preserve the user version */ BTREE_APPLICATION_ID, 0, /* Preserve the application id */ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) ); /* Copy Btree meta values */ for(i=0; i<ArraySize(aCopy); i+=2){ /* GetMeta() and UpdateMeta() cannot fail in this context because ** we already have page 1 loaded into cache and marked dirty. */ sqlite3BtreeGetMeta(pMain, aCopy[i], &meta); rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]); if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum; } if( pOut==0 ){ rc = sqlite3BtreeCopyFile(pMain, pTemp); } if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; #ifndef SQLITE_OMIT_AUTOVACUUM if( pOut==0 ){ sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp)); } #endif } assert( rc==SQLITE_OK ); if( pOut==0 ){ rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1); } end_of_vacuum: /* Restore the original value of db->flags */ db->init.iDb = 0; db->mDbFlags = saved_mDbFlags; db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->mTrace = saved_mTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; if( pDb ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } /* This both clears the schemas and reduces the size of the db->aDb[] ** array. */ sqlite3ResetAllSchemasOfConnection(db); return rc; }