void testOverwrite1(){ OsFile id;/* I don't like the implementation since OSFile need to create by myself*/ int Readonly; int i,nPages; double time; char * buf=sqlite3Malloc(config.pagesize); for(nPages=1; nPages<=config.pagenum; nPages++){ printf("write %d pages!\n",nPages); rc = sqlite3OsOpenReadWrite( config.datfile, &id, &Readonly); errorHandle(rc, "can't open the file"); start_timer(); for(i=0;i<nPages;i++){ sqlite3Randomness(config.pagesize, buf); rc = sqlite3OsWrite(&id, buf, config.pagesize); errorHandle(rc, "write error"); } time = get_timer(); pr_times(config.recordfile, time); rc= sqlite3OsClose(&id); errorHandle(rc, "can't close the file"); rc = sqlite3OsDelete(config.datfile); errorHandle(rc, "can't delete the file"); } //TODO can't find the defintion, do it later //sqlite3Free((void *)buf); }
/* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int nPath = strlen(zPath); if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){ /* Deleting a journal file. The end of a transaction. */ jt_file *pMain = locateDatabaseHandle(zPath); if( pMain ){ closeTransaction(pMain); } } return sqlite3OsDelete(g.pVfs, zPath, dirSync); }
/* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc = SQLITE_OK; Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ tvfsExecTcl(p, "xDelete", Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync); } return rc; }
//Lock/unlock tests void Test5() { 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 res = 0; int 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); //Lock/unlock //SHARED_LOCK err = sqlite3OsLock(osFile, SHARED_LOCK); TEST2(err, SQLITE_OK); err = sqlite3OsCheckReservedLock(osFile, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); //RESERVED_LOCK err = sqlite3OsLock(osFile, RESERVED_LOCK); TEST2(err, SQLITE_OK); err = sqlite3OsCheckReservedLock(osFile, &res); TEST2(err, SQLITE_OK); TEST2(res, 1); //PENDING_LOCK err = sqlite3OsLock(osFile, PENDING_LOCK); TEST2(err, SQLITE_OK); //EXCLUSIVE_LOCK err = sqlite3OsLock(osFile, EXCLUSIVE_LOCK); TEST2(err, SQLITE_OK); //back to SHARED_LOCK err = sqlite3OsLock(osFile, SHARED_LOCK); TEST2(err, SQLITE_OK); //UNLOCK err = sqlite3OsUnlock(osFile, NO_LOCK); //Close the file err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); // err = sqlite3OsDelete(vfs, KTestFile1Z, 0); TEST2(err, SQLITE_OK); User::Free(osFile); }
/* ** This routine implements the OP_Vacuum opcode of the VDBE. */ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ int rc = SQLITE_OK; /* Return code from service routines */ const char *zFilename; /* full pathname of the database file */ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; char *zSql = 0; int saved_flags; /* Saved value of the db->flags */ Db *pDb = 0; /* Database to detach at end of vacuum */ /* Save the current value of the write-schema flag before setting it. */ saved_flags = db->flags; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", (char*)0); rc = SQLITE_ERROR; goto end_of_vacuum; } /* Get the full pathname of the database file and create a ** temporary filename in the same directory as the original file. */ pMain = db->aDb[0].pBt; zFilename = sqlite3BtreeGetFilename(pMain); assert( zFilename ); if( zFilename[0]=='\0' ){ /* The in-memory database. Do nothing. Return directly to avoid causing ** an error trying to DETACH the vacuum_db (which never got attached) ** in the exit-handler. */ return SQLITE_OK; } nFilename = strlen(zFilename); zTemp = sqliteMalloc( nFilename+100 ); if( zTemp==0 ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } strcpy(zTemp, zFilename); /* The randomName() procedure in the following loop uses an excellent ** source of randomness to generate a name from a space of 1.3e+31 ** possibilities. So unless the directory already contains on the order ** of 1.3e+31 files, the probability that the following loop will ** run more than once or twice is vanishingly small. We are certain ** enough that this loop will always terminate (and terminate quickly) ** that we don't even bother to set a maximum loop count. */ do { zTemp[nFilename] = '-'; randomName((unsigned char*)&zTemp[nFilename+1]); } while( sqlite3OsFileExists(zTemp) ); /* 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. */ zSql = sqlite3MPrintf("ATTACH '%q' AS vacuum_db;", zTemp); if( !zSql ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } rc = execSql(db, zSql); sqliteFree(zSql); zSql = 0; if( rc!=SQLITE_OK ) goto end_of_vacuum; pDb = &db->aDb[db->nDb-1]; assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), sqlite3BtreeGetReserve(pMain)); assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) ); rc = execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); if( rc!=SQLITE_OK ){ goto end_of_vacuum; } #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, sqlite3BtreeGetAutoVacuum(pMain)); #endif /* Begin a transaction */ rc = execSql(db, "BEGIN EXCLUSIVE;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ rc = execExecSql(db, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" " AND rootpage>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000)" " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) " " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy ** the contents to the temporary database. */ rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM ' || quote(name) || ';'" "FROM sqlite_master " "WHERE type = 'table' AND name!='sqlite_sequence' " " AND rootpage>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy over the sequence table */ rc = execExecSql(db, "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' " ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM ' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" ); 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 = execSql(db, "INSERT INTO vacuum_db.sqlite_master " " SELECT type, name, tbl_name, rootpage, sql" " FROM sqlite_master" " WHERE type='view' OR type='trigger'" " OR (type='table' AND rootpage=0)" ); if( rc ) goto end_of_vacuum; /* At this point, unless the main db was completely empty, there is now a ** transaction open on the vacuum database, but not on the main database. ** Open a btree level transaction on the main database. This allows a ** call to sqlite3BtreeCopyFile(). The main database btree level ** transaction is then committed, so the SQL level never knows it was ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ if( rc==SQLITE_OK ){ 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[] = { 1, 1, /* Add one to the old schema cookie */ 3, 0, /* Preserve the default page cache size */ 5, 0, /* Preserve the default text encoding */ 6, 0, /* Preserve the user version */ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); assert( 1==sqlite3BtreeIsInTrans(pMain) ); /* Copy Btree meta values */ for(i=0; i<sizeof(aCopy)/sizeof(aCopy[0]); i+=2){ rc = sqlite3BtreeGetMeta(pMain, aCopy[i], &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]); if( rc!=SQLITE_OK ) goto end_of_vacuum; } rc = sqlite3BtreeCopyFile(pMain, pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pMain); } end_of_vacuum: /* Restore the original value of db->flags */ db->flags = saved_flags; /* 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 ){ sqlite3MallocDisallow(); sqlite3BtreeClose(pDb->pBt); sqlite3MallocAllow(); pDb->pBt = 0; pDb->pSchema = 0; } if( zTemp ){ sqlite3OsDelete(zTemp); sqliteFree(zTemp); } sqliteFree( zSql ); sqlite3ResetInternalSchema(db, 0); return rc; }
/* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int devsymDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ return sqlite3OsDelete(g.pVfs, zPath, dirSync); }
/* ** A read or write transaction may or may not be active on database handle ** db. If a transaction is active, commit it. If there is a ** write-transaction spanning more than one database file, this routine ** takes care of the master journal trickery. */ static int vdbeCommit(sqlite3 *db){ int i; int nTrans = 0; /* Number of databases with an active write-transaction */ int rc = SQLITE_OK; int needXcommit = 0; for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; } } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ int rc; sqlite3SafetyOff(db); rc = db->xCommitCallback(db->pCommitArg); sqlite3SafetyOn(db); if( rc ){ return SQLITE_CONSTRAINT; } } /* The simple case - no more than one database file (not counting the ** TEMP database) has a transaction active. There is no need for the ** master-journal. ** ** If the return value of sqlite3BtreeGetFilename() is a zero length ** string, it means the main database is :memory:. In that case we do ** not support atomic multi-file commits, so use the simple case then ** too. */ if( 0==strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = sqlite3BtreeSync(pBt, 0); } } /* Do the commit only if all databases successfully synced */ if( rc==SQLITE_OK ){ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommit(pBt); } } } } /* The complex case - There is a multi-file write-transaction active. ** This requires a master journal file to ensure the transaction is ** committed atomicly. */ else{ char *zMaster = 0; /* File-name for the master journal */ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); OsFile master; /* Select a master journal file name */ do { u32 random; sqliteFree(zMaster); sqlite3Randomness(sizeof(random), &random); zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random&0x7fffffff); if( !zMaster ){ return SQLITE_NOMEM; } }while( sqlite3OsFileExists(zMaster) ); /* Open the master journal. */ memset(&master, 0, sizeof(master)); rc = sqlite3OsOpenExclusive(zMaster, &master, 0); if( rc!=SQLITE_OK ){ sqliteFree(zMaster); return rc; } /* Write the name of each database file in the transaction into the new ** master journal file. If an error occurs at this point close ** and delete the master journal file. All the individual journal files ** still have 'null' as the master journal pointer, so they will roll ** back independantly if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( i==1 ) continue; /* Ignore the TEMP database */ if( pBt && sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); if( zFile[0]==0 ) continue; /* Ignore :memory: databases */ rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqlite3OsDelete(zMaster); sqliteFree(zMaster); return rc; } } } /* Sync the master journal file. Before doing this, open the directory ** the master journal file is store in so that it gets synced too. */ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); rc = sqlite3OsOpenDirectory(zMainFile, &master); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqlite3OsDelete(zMaster); sqliteFree(zMaster); return rc; } rc = sqlite3OsSync(&master); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqliteFree(zMaster); return rc; } /* Sync all the db files involved in the transaction. The same call ** sets the master journal pointer in each individual journal. If ** an error occurs here, do not delete the master journal file. ** ** If the error occurs during the first call to sqlite3BtreeSync(), ** then there is a chance that the master journal file will be ** orphaned. But we cannot delete it, in case the master journal ** file name was written into the journal file before the failure ** occured. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ rc = sqlite3BtreeSync(pBt, zMaster); if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqliteFree(zMaster); return rc; } } } sqlite3OsClose(&master); /* Delete the master journal file. This commits the transaction. After ** doing this the directory is synced again before any individual ** transaction files are deleted. */ rc = sqlite3OsDelete(zMaster); assert( rc==SQLITE_OK ); sqliteFree(zMaster); zMaster = 0; rc = sqlite3OsSyncDirectory(zMainFile); if( rc!=SQLITE_OK ){ /* This is not good. The master journal file has been deleted, but ** the directory sync failed. There is no completely safe course of ** action from here. The individual journals contain the name of the ** master journal file, but there is no way of knowing if that ** master journal exists now or if it will exist after the operating ** system crash that may follow the fsync() failure. */ assert(0); sqliteFree(zMaster); return rc; } /* All files and directories have already been synced, so the following ** calls to sqlite3BtreeCommit() are only closing files and deleting ** journals. If something goes wrong while this is happening we don't ** really care. The integrity of the transaction is already guaranteed, ** but some stray 'cold' journals may be lying around. Returning an ** error code won't help matters. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommit(pBt); } } } return rc; }
//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); }
//Create/open/close/delete a file void Test1() { 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 outFlags = 0; int err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, &outFlags); TEST2(err, SQLITE_OK); TEST(outFlags & SQLITE_OPEN_READWRITE); err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //Opening an existing file for R/W err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE, &outFlags); TEST2(err, SQLITE_OK); TEST(outFlags & SQLITE_OPEN_READWRITE); err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //Opening a read-only file err = sqlite3OsOpen(vfs, KTestFile2Z, osFile, SQLITE_OPEN_READWRITE, &outFlags); TEST2(err, SQLITE_OK); TEST(outFlags & SQLITE_OPEN_READONLY); //Truncate a read-only file err = osFile->pMethods->xTruncate(osFile, 0); TEST2(err, SQLITE_IOERR); //xAccess - read-only file int res = 0; err = vfs->xAccess(vfs, KTestFile2Z, SQLITE_ACCESS_READ, &res); TEST2(err, SQLITE_OK); TEST(res != 0); //xAccess - invalid request res = 0; err = vfs->xAccess(vfs, KTestFile2Z, 122, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); // err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //Creating a new file err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, &outFlags); TEST2(err, SQLITE_OK); TEST(outFlags & SQLITE_OPEN_READWRITE); err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //Open a file for a read-only access err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READONLY, &outFlags); TEST2(err, SQLITE_OK); TEST(outFlags & SQLITE_OPEN_READONLY); err = sqlite3OsWrite(osFile, "1234", 4, 0); TEST(err != SQLITE_OK); err = sqlite3SymbianLastOsError(); TEST2(err, KErrAccessDenied); err = vfs->xGetLastError(vfs, 0, 0); TEST2(err, 0);//Default implementation err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //Delete KTestFile3Z file err = sqlite3OsDelete(vfs, KTestFile3Z, 0); TEST2(err, SQLITE_OK); res = 0; err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); //Open a file for an exclusive access err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE, &outFlags); TEST2(err, SQLITE_OK); err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //The file should not exist now err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); //Open a file for an exclusive access without deleting it after err = sqlite3OsOpen(vfs, KTestFile3Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE, &outFlags); TEST2(err, SQLITE_OK); err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); //The file should exist now err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 1); //Delete KTestFile3Z file err = sqlite3OsDelete(vfs, KTestFile3Z, 0); TEST2(err, SQLITE_OK); err = sqlite3OsAccess(vfs, KTestFile3Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); // User::Free(osFile); }
/* ** This routine implements the OP_Vacuum opcode of the VDBE. */ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ int rc = SQLITE_OK; /* Return code from service routines */ #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM const char *zFilename; /* full pathname of the database file */ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ int i; /* Loop counter */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; char *zSql = 0; if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", (char*)0); rc = SQLITE_ERROR; goto end_of_vacuum; } /* Get the full pathname of the database file and create a ** temporary filename in the same directory as the original file. */ pMain = db->aDb[0].pBt; zFilename = sqlite3BtreeGetFilename(pMain); assert( zFilename ); if( zFilename[0]=='\0' ){ /* The in-memory database. Do nothing. Return directly to avoid causing ** an error trying to DETACH the vacuum_db (which never got attached) ** in the exit-handler. */ return SQLITE_OK; } nFilename = strlen(zFilename); zTemp = sqliteMalloc( nFilename+100 ); if( zTemp==0 ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } strcpy(zTemp, zFilename); i = 0; do { zTemp[nFilename] = '-'; randomName((unsigned char*)&zTemp[nFilename+1]); } while( i<10 && sqlite3OsFileExists(zTemp) ); /* 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. */ zSql = sqlite3MPrintf("ATTACH '%q' AS vacuum_db;", zTemp); if( !zSql ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } rc = execSql(db, zSql); sqliteFree(zSql); zSql = 0; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), sqlite3BtreeGetReserve(pMain)); assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) ); execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); /* Begin a transaction */ rc = execSql(db, "BEGIN;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ rc = execExecSql(db, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " " FROM sqlite_master WHERE type='table' " "UNION ALL " "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000) " " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' " "UNION ALL " "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) " " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'" "UNION ALL " "SELECT 'CREATE VIEW vacuum_db.' || substr(sql,13,100000000) " " FROM sqlite_master WHERE type='view'" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy ** the contents to the temporary database. */ rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM ' || quote(name) || ';'" "FROM sqlite_master " "WHERE type = 'table';" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy the triggers from the main database to the temporary database. ** This was deferred before in case the triggers interfered with copying ** the data. It's possible the indices should be deferred until this ** point also. */ rc = execExecSql(db, "SELECT 'CREATE TRIGGER vacuum_db.' || substr(sql, 16, 1000000) " "FROM sqlite_master WHERE type='trigger'" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* At this point, unless the main db was completely empty, there is now a ** transaction open on the vacuum database, but not on the main database. ** Open a btree level transaction on the main database. This allows a ** call to sqlite3BtreeCopyFile(). The main database btree level ** transaction is then committed, so the SQL level never knows it was ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ if( sqlite3BtreeIsInTrans(pTemp) ){ u32 meta; assert( 0==sqlite3BtreeIsInTrans(pMain) ); rc = sqlite3BtreeBeginTrans(pMain, 1); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta ** values 2 and 3, the default values of a couple of pragmas. */ rc = sqlite3BtreeGetMeta(pMain, 3, &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeGetMeta(pMain, 4, &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCopyFile(pMain, pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pMain); } end_of_vacuum: /* 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( rc==SQLITE_OK ){ rc = execSql(db, "DETACH vacuum_db;"); }else{ execSql(db, "DETACH vacuum_db;"); } if( zTemp ){ sqlite3OsDelete(zTemp); sqliteFree(zTemp); } if( zSql ) sqliteFree( zSql ); sqlite3ResetInternalSchema(db, 0); #endif return rc; }