/* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ int sqlite3Os2TempFileName( char *zBuf ){ static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; PSZ zTempPath = 0; if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ ULONG ulDriveNum = 0, ulDriveMap = 0; DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); } } } for(;;){ sprintf( zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath ); j = strlen( zBuf ); sqlite3Randomness( 15, &zBuf[j] ); for( i = 0; i < 15; i++, j++ ){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; if( !sqlite3OsFileExists( zBuf ) ) break; } OSTRACE2( "TEMP FILENAME: %s\n", zBuf ); return SQLITE_OK; }
/* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ int sqlite3WinTempFileName(char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; char zTempPath[SQLITE_TEMPNAME_SIZE]; if( sqlite3_temp_directory ){ strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30); zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; }else if( isNT() ){ char *zMulti; WCHAR zWidePath[SQLITE_TEMPNAME_SIZE]; GetTempPathW(SQLITE_TEMPNAME_SIZE-30, zWidePath); zMulti = unicodeToUtf8(zWidePath); if( zMulti ){ strncpy(zTempPath, zMulti, SQLITE_TEMPNAME_SIZE-30); zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; sqliteFree(zMulti); }else{ return SQLITE_NOMEM; } }else{ char *zUtf8; char zMbcsPath[SQLITE_TEMPNAME_SIZE]; GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zMbcsPath); zUtf8 = mbcsToUtf8(zMbcsPath); if( zUtf8 ){ strncpy(zTempPath, zUtf8, SQLITE_TEMPNAME_SIZE-30); zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; sqliteFree(zUtf8); }else{ return SQLITE_NOMEM; } } for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; for(;;){ sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath); j = strlen(zBuf); sqlite3Randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; if( !sqlite3OsFileExists(zBuf) ) break; } TRACE2("TEMP FILENAME: %s\n", zBuf); return SQLITE_OK; }
/* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ int sqlite3Os2TempFileName( char *zBuf ){ static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; PSZ zTempPath = 0; if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ ULONG ulDriveNum = 0, ulDriveMap = 0; DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); } } } /* strip off a trailing slashes or backslashes, otherwise we would get * * multiple (back)slashes which causes DosOpen() to fail */ j = strlen(zTempPath); while( j > 0 && zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ){ j--; } zTempPath[j] = '\0'; for(;;){ sprintf( zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath ); j = strlen( zBuf ); sqlite3Randomness( 15, &zBuf[j] ); for( i = 0; i < 15; i++, j++ ){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; if( !sqlite3OsFileExists( zBuf ) ) break; } OSTRACE2( "TEMP FILENAME: %s\n", zBuf ); return SQLITE_OK; }
/* ** 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; }
/* ** 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; }
/* ** Prepare a virtual machine for execution. This involves things such ** as allocating stack space and initializing the program counter. ** After the VDBE has be prepped, it can be executed by one or more ** calls to sqlite3VdbeExec(). ** ** This is the only way to move a VDBE from VDBE_MAGIC_INIT to ** VDBE_MAGIC_RUN. */ void sqlite3VdbeMakeReady( Vdbe *p, /* The VDBE */ int nVar, /* Number of '?' see in the SQL statement */ int nMem, /* Number of memory cells to allocate */ int nCursor, /* Number of cursors to allocate */ int isExplain /* True if the EXPLAIN keywords is present */ ){ int n; assert( p!=0 ); assert( p->magic==VDBE_MAGIC_INIT ); /* There should be at least one opcode. */ assert( p->nOp>0 ); /* No instruction ever pushes more than a single element onto the ** stack. And the stack never grows on successive executions of the ** same loop. So the total number of instructions is an upper bound ** on the maximum stack depth required. ** ** Allocation all the stack space we will ever need. */ if( p->aStack==0 ){ resolveP2Values(p); assert( nVar>=0 ); n = isExplain ? 10 : p->nOp; p->aStack = sqliteMalloc( n*sizeof(p->aStack[0]) /* aStack */ + n*sizeof(Mem*) /* apArg */ + nVar*sizeof(Mem) /* aVar */ + nVar*sizeof(char*) /* azVar */ + nMem*sizeof(Mem) /* aMem */ + nCursor*sizeof(Cursor*) /* apCsr */ ); if( !sqlite3_malloc_failed ){ p->aMem = &p->aStack[n]; p->nMem = nMem; p->aVar = &p->aMem[nMem]; p->nVar = nVar; p->okVar = 0; p->apArg = (Mem**)&p->aVar[nVar]; p->azVar = (char**)&p->apArg[n]; p->apCsr = (Cursor**)&p->azVar[nVar]; p->nCursor = nCursor; for(n=0; n<nVar; n++){ p->aVar[n].flags = MEM_Null; } for(n=0; n<nMem; n++){ p->aMem[n].flags = MEM_Null; } } } #ifdef SQLITE_DEBUG if( (p->db->flags & SQLITE_VdbeListing)!=0 || sqlite3OsFileExists("vdbe_explain") ){ int i; printf("VDBE Program Listing:\n"); sqlite3VdbePrintSql(p); for(i=0; i<p->nOp; i++){ sqlite3VdbePrintOp(stdout, i, &p->aOp[i]); } } if( sqlite3OsFileExists("vdbe_trace") ){ p->trace = stdout; } #endif p->pTos = &p->aStack[-1]; p->pc = -1; p->rc = SQLITE_OK; p->uniqueCnt = 0; p->returnDepth = 0; p->errorAction = OE_Abort; p->popStack = 0; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; #ifdef VDBE_PROFILE { int i; for(i=0; i<p->nOp; i++){ p->aOp[i].cnt = 0; p->aOp[i].cycles = 0; } } #endif }
/* ** 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; }
/* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ int sqlite3OsTempFileName(char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; char zTempPath[SQLITE_TEMPNAME_SIZE]; if( sqlite3_temp_directory ){ strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30); zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; }else{ GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zTempPath); } for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; for(;;){ sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath); j = strlen(zBuf); sqlite3Randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; if( !sqlite3OsFileExists(zBuf) ) break; } TRACE2("TEMP FILENAME: %s\n", zBuf); return SQLITE_OK; }