xref: /illumos-gate/usr/src/lib/libsqlite/src/vacuum.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate ** 2003 April 6
37c478bd9Sstevel@tonic-gate **
47c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
57c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
67c478bd9Sstevel@tonic-gate **
77c478bd9Sstevel@tonic-gate **    May you do good and not evil.
87c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
97c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
107c478bd9Sstevel@tonic-gate **
117c478bd9Sstevel@tonic-gate *************************************************************************
127c478bd9Sstevel@tonic-gate ** This file contains code used to implement the VACUUM command.
137c478bd9Sstevel@tonic-gate **
147c478bd9Sstevel@tonic-gate ** Most of the code in this file may be omitted by defining the
157c478bd9Sstevel@tonic-gate ** SQLITE_OMIT_VACUUM macro.
167c478bd9Sstevel@tonic-gate **
177c478bd9Sstevel@tonic-gate ** $Id: vacuum.c,v 1.13.2.2 2004/06/04 19:07:54 drh Exp $
187c478bd9Sstevel@tonic-gate */
197c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
207c478bd9Sstevel@tonic-gate #include "os.h"
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate ** A structure for holding a dynamic string - a string that can grow
24*1da57d55SToomas Soome ** without bound.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate typedef struct dynStr dynStr;
277c478bd9Sstevel@tonic-gate struct dynStr {
287c478bd9Sstevel@tonic-gate   char *z;        /* Text of the string in space obtained from sqliteMalloc() */
297c478bd9Sstevel@tonic-gate   int nAlloc;     /* Amount of space allocated to z[] */
307c478bd9Sstevel@tonic-gate   int nUsed;      /* Next unused slot in z[] */
317c478bd9Sstevel@tonic-gate };
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate ** A structure that holds the vacuum context
357c478bd9Sstevel@tonic-gate */
367c478bd9Sstevel@tonic-gate typedef struct vacuumStruct vacuumStruct;
377c478bd9Sstevel@tonic-gate struct vacuumStruct {
387c478bd9Sstevel@tonic-gate   sqlite *dbOld;       /* Original database */
397c478bd9Sstevel@tonic-gate   sqlite *dbNew;       /* New database */
407c478bd9Sstevel@tonic-gate   char **pzErrMsg;     /* Write errors here */
417c478bd9Sstevel@tonic-gate   int rc;              /* Set to non-zero on an error */
427c478bd9Sstevel@tonic-gate   const char *zTable;  /* Name of a table being copied */
437c478bd9Sstevel@tonic-gate   const char *zPragma; /* Pragma to execute with results */
447c478bd9Sstevel@tonic-gate   dynStr s1, s2;       /* Two dynamic strings */
457c478bd9Sstevel@tonic-gate };
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
487c478bd9Sstevel@tonic-gate /*
497c478bd9Sstevel@tonic-gate ** Append text to a dynamic string
507c478bd9Sstevel@tonic-gate */
appendText(dynStr * p,const char * zText,int nText)517c478bd9Sstevel@tonic-gate static void appendText(dynStr *p, const char *zText, int nText){
527c478bd9Sstevel@tonic-gate   if( nText<0 ) nText = strlen(zText);
537c478bd9Sstevel@tonic-gate   if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
547c478bd9Sstevel@tonic-gate     char *zNew;
557c478bd9Sstevel@tonic-gate     p->nAlloc = p->nUsed + nText + 1000;
567c478bd9Sstevel@tonic-gate     zNew = sqliteRealloc(p->z, p->nAlloc);
577c478bd9Sstevel@tonic-gate     if( zNew==0 ){
587c478bd9Sstevel@tonic-gate       sqliteFree(p->z);
597c478bd9Sstevel@tonic-gate       memset(p, 0, sizeof(*p));
607c478bd9Sstevel@tonic-gate       return;
617c478bd9Sstevel@tonic-gate     }
627c478bd9Sstevel@tonic-gate     p->z = zNew;
637c478bd9Sstevel@tonic-gate   }
647c478bd9Sstevel@tonic-gate   memcpy(&p->z[p->nUsed], zText, nText+1);
657c478bd9Sstevel@tonic-gate   p->nUsed += nText;
667c478bd9Sstevel@tonic-gate }
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate ** Append text to a dynamic string, having first put the text in quotes.
707c478bd9Sstevel@tonic-gate */
appendQuoted(dynStr * p,const char * zText)717c478bd9Sstevel@tonic-gate static void appendQuoted(dynStr *p, const char *zText){
727c478bd9Sstevel@tonic-gate   int i, j;
737c478bd9Sstevel@tonic-gate   appendText(p, "'", 1);
747c478bd9Sstevel@tonic-gate   for(i=j=0; zText[i]; i++){
757c478bd9Sstevel@tonic-gate     if( zText[i]=='\'' ){
767c478bd9Sstevel@tonic-gate       appendText(p, &zText[j], i-j+1);
777c478bd9Sstevel@tonic-gate       j = i + 1;
787c478bd9Sstevel@tonic-gate       appendText(p, "'", 1);
797c478bd9Sstevel@tonic-gate     }
807c478bd9Sstevel@tonic-gate   }
817c478bd9Sstevel@tonic-gate   if( j<i ){
827c478bd9Sstevel@tonic-gate     appendText(p, &zText[j], i-j);
837c478bd9Sstevel@tonic-gate   }
847c478bd9Sstevel@tonic-gate   appendText(p, "'", 1);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate ** Execute statements of SQL.  If an error occurs, write the error
897c478bd9Sstevel@tonic-gate ** message into *pzErrMsg and return non-zero.
907c478bd9Sstevel@tonic-gate */
execsql(char ** pzErrMsg,sqlite * db,const char * zSql)91*1da57d55SToomas Soome static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
927c478bd9Sstevel@tonic-gate   char *zErrMsg = 0;
937c478bd9Sstevel@tonic-gate   int rc;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate   /* printf("***** executing *****\n%s\n", zSql); */
967c478bd9Sstevel@tonic-gate   rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
977c478bd9Sstevel@tonic-gate   if( zErrMsg ){
987c478bd9Sstevel@tonic-gate     sqliteSetString(pzErrMsg, zErrMsg, (char*)0);
997c478bd9Sstevel@tonic-gate     sqlite_freemem(zErrMsg);
1007c478bd9Sstevel@tonic-gate   }
1017c478bd9Sstevel@tonic-gate   return rc;
1027c478bd9Sstevel@tonic-gate }
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate ** This is the second stage callback.  Each invocation contains all the
1067c478bd9Sstevel@tonic-gate ** data for a single row of a single table in the original database.  This
1077c478bd9Sstevel@tonic-gate ** routine must write that information into the new database.
1087c478bd9Sstevel@tonic-gate */
vacuumCallback2(void * pArg,int argc,char ** argv,char ** NotUsed)1097c478bd9Sstevel@tonic-gate static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
1107c478bd9Sstevel@tonic-gate   vacuumStruct *p = (vacuumStruct*)pArg;
1117c478bd9Sstevel@tonic-gate   const char *zSep = "(";
1127c478bd9Sstevel@tonic-gate   int i;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate   if( argv==0 ) return 0;
1157c478bd9Sstevel@tonic-gate   p->s2.nUsed = 0;
1167c478bd9Sstevel@tonic-gate   appendText(&p->s2, "INSERT INTO ", -1);
1177c478bd9Sstevel@tonic-gate   appendQuoted(&p->s2, p->zTable);
1187c478bd9Sstevel@tonic-gate   appendText(&p->s2, " VALUES", -1);
1197c478bd9Sstevel@tonic-gate   for(i=0; i<argc; i++){
1207c478bd9Sstevel@tonic-gate     appendText(&p->s2, zSep, 1);
1217c478bd9Sstevel@tonic-gate     zSep = ",";
1227c478bd9Sstevel@tonic-gate     if( argv[i]==0 ){
1237c478bd9Sstevel@tonic-gate       appendText(&p->s2, "NULL", 4);
1247c478bd9Sstevel@tonic-gate     }else{
1257c478bd9Sstevel@tonic-gate       appendQuoted(&p->s2, argv[i]);
1267c478bd9Sstevel@tonic-gate     }
1277c478bd9Sstevel@tonic-gate   }
1287c478bd9Sstevel@tonic-gate   appendText(&p->s2,")", 1);
1297c478bd9Sstevel@tonic-gate   p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
1307c478bd9Sstevel@tonic-gate   return p->rc;
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate /*
1347c478bd9Sstevel@tonic-gate ** This is the first stage callback.  Each invocation contains three
1357c478bd9Sstevel@tonic-gate ** arguments where are taken from the SQLITE_MASTER table of the original
1367c478bd9Sstevel@tonic-gate ** database:  (1) the entry type, (2) the entry name, and (3) the SQL for
1377c478bd9Sstevel@tonic-gate ** the entry.  In all cases, execute the SQL of the third argument.
138*1da57d55SToomas Soome ** For tables, run a query to select all entries in that table and
1397c478bd9Sstevel@tonic-gate ** transfer them to the second-stage callback.
1407c478bd9Sstevel@tonic-gate */
vacuumCallback1(void * pArg,int argc,char ** argv,char ** NotUsed)1417c478bd9Sstevel@tonic-gate static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
1427c478bd9Sstevel@tonic-gate   vacuumStruct *p = (vacuumStruct*)pArg;
1437c478bd9Sstevel@tonic-gate   int rc = 0;
1447c478bd9Sstevel@tonic-gate   assert( argc==3 );
1457c478bd9Sstevel@tonic-gate   if( argv==0 ) return 0;
1467c478bd9Sstevel@tonic-gate   assert( argv[0]!=0 );
1477c478bd9Sstevel@tonic-gate   assert( argv[1]!=0 );
1487c478bd9Sstevel@tonic-gate   assert( argv[2]!=0 );
1497c478bd9Sstevel@tonic-gate   rc = execsql(p->pzErrMsg, p->dbNew, argv[2]);
1507c478bd9Sstevel@tonic-gate   if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
1517c478bd9Sstevel@tonic-gate     char *zErrMsg = 0;
1527c478bd9Sstevel@tonic-gate     p->s1.nUsed = 0;
1537c478bd9Sstevel@tonic-gate     appendText(&p->s1, "SELECT * FROM ", -1);
1547c478bd9Sstevel@tonic-gate     appendQuoted(&p->s1, argv[1]);
1557c478bd9Sstevel@tonic-gate     p->zTable = argv[1];
1567c478bd9Sstevel@tonic-gate     rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
1577c478bd9Sstevel@tonic-gate     if( zErrMsg ){
1587c478bd9Sstevel@tonic-gate       sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
1597c478bd9Sstevel@tonic-gate       sqlite_freemem(zErrMsg);
1607c478bd9Sstevel@tonic-gate     }
1617c478bd9Sstevel@tonic-gate   }
1627c478bd9Sstevel@tonic-gate   if( rc!=SQLITE_ABORT ) p->rc = rc;
1637c478bd9Sstevel@tonic-gate   return rc;
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /*
1677c478bd9Sstevel@tonic-gate ** This callback is used to transfer PRAGMA settings from one database
1687c478bd9Sstevel@tonic-gate ** to the other.  The value in argv[0] should be passed to a pragma
1697c478bd9Sstevel@tonic-gate ** identified by ((vacuumStruct*)pArg)->zPragma.
1707c478bd9Sstevel@tonic-gate */
vacuumCallback3(void * pArg,int argc,char ** argv,char ** NotUsed)1717c478bd9Sstevel@tonic-gate static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
1727c478bd9Sstevel@tonic-gate   vacuumStruct *p = (vacuumStruct*)pArg;
1737c478bd9Sstevel@tonic-gate   char zBuf[200];
1747c478bd9Sstevel@tonic-gate   assert( argc==1 );
1757c478bd9Sstevel@tonic-gate   if( argv==0 ) return 0;
1767c478bd9Sstevel@tonic-gate   assert( argv[0]!=0 );
1777c478bd9Sstevel@tonic-gate   assert( strlen(p->zPragma)<100 );
1787c478bd9Sstevel@tonic-gate   assert( strlen(argv[0])<30 );
1797c478bd9Sstevel@tonic-gate   sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
1807c478bd9Sstevel@tonic-gate   p->rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
1817c478bd9Sstevel@tonic-gate   return p->rc;
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate ** Generate a random name of 20 character in length.
1867c478bd9Sstevel@tonic-gate */
randomName(unsigned char * zBuf)1877c478bd9Sstevel@tonic-gate static void randomName(unsigned char *zBuf){
1887c478bd9Sstevel@tonic-gate   static const unsigned char zChars[] =
1897c478bd9Sstevel@tonic-gate     "abcdefghijklmnopqrstuvwxyz"
1907c478bd9Sstevel@tonic-gate     "0123456789";
1917c478bd9Sstevel@tonic-gate   int i;
1927c478bd9Sstevel@tonic-gate   sqliteRandomness(20, zBuf);
1937c478bd9Sstevel@tonic-gate   for(i=0; i<20; i++){
1947c478bd9Sstevel@tonic-gate     zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
1957c478bd9Sstevel@tonic-gate   }
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate #endif
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate ** The non-standard VACUUM command is used to clean up the database,
2017c478bd9Sstevel@tonic-gate ** collapse free space, etc.  It is modelled after the VACUUM command
2027c478bd9Sstevel@tonic-gate ** in PostgreSQL.
2037c478bd9Sstevel@tonic-gate **
2047c478bd9Sstevel@tonic-gate ** In version 1.0.x of SQLite, the VACUUM command would call
2057c478bd9Sstevel@tonic-gate ** gdbm_reorganize() on all the database tables.  But beginning
2067c478bd9Sstevel@tonic-gate ** with 2.0.0, SQLite no longer uses GDBM so this command has
2077c478bd9Sstevel@tonic-gate ** become a no-op.
2087c478bd9Sstevel@tonic-gate */
sqliteVacuum(Parse * pParse,Token * pTableName)2097c478bd9Sstevel@tonic-gate void sqliteVacuum(Parse *pParse, Token *pTableName){
2107c478bd9Sstevel@tonic-gate   Vdbe *v = sqliteGetVdbe(pParse);
2117c478bd9Sstevel@tonic-gate   sqliteVdbeAddOp(v, OP_Vacuum, 0, 0);
2127c478bd9Sstevel@tonic-gate   return;
2137c478bd9Sstevel@tonic-gate }
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate /*
2167c478bd9Sstevel@tonic-gate ** This routine implements the OP_Vacuum opcode of the VDBE.
2177c478bd9Sstevel@tonic-gate */
sqliteRunVacuum(char ** pzErrMsg,sqlite * db)2187c478bd9Sstevel@tonic-gate int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
2197c478bd9Sstevel@tonic-gate #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
2207c478bd9Sstevel@tonic-gate   const char *zFilename;  /* full pathname of the database file */
2217c478bd9Sstevel@tonic-gate   int nFilename;          /* number of characters  in zFilename[] */
2227c478bd9Sstevel@tonic-gate   char *zTemp = 0;        /* a temporary file in same directory as zFilename */
2237c478bd9Sstevel@tonic-gate   sqlite *dbNew = 0;      /* The new vacuumed database */
2247c478bd9Sstevel@tonic-gate   int rc = SQLITE_OK;     /* Return code from service routines */
2257c478bd9Sstevel@tonic-gate   int i;                  /* Loop counter */
2267c478bd9Sstevel@tonic-gate   char *zErrMsg;          /* Error message */
2277c478bd9Sstevel@tonic-gate   vacuumStruct sVac;      /* Information passed to callbacks */
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate   /* These are all of the pragmas that need to be transferred over
2307c478bd9Sstevel@tonic-gate   ** to the new database */
2317c478bd9Sstevel@tonic-gate   static const char *zPragma[] = {
2327c478bd9Sstevel@tonic-gate      "default_synchronous",
2337c478bd9Sstevel@tonic-gate      "default_cache_size",
2347c478bd9Sstevel@tonic-gate      /* "default_temp_store", */
2357c478bd9Sstevel@tonic-gate   };
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate   if( db->flags & SQLITE_InTrans ){
238*1da57d55SToomas Soome     sqliteSetString(pzErrMsg, "cannot VACUUM from within a transaction",
2397c478bd9Sstevel@tonic-gate        (char*)0);
2407c478bd9Sstevel@tonic-gate     return SQLITE_ERROR;
2417c478bd9Sstevel@tonic-gate   }
2427c478bd9Sstevel@tonic-gate   if( db->flags & SQLITE_Interrupt ){
2437c478bd9Sstevel@tonic-gate     return SQLITE_INTERRUPT;
2447c478bd9Sstevel@tonic-gate   }
2457c478bd9Sstevel@tonic-gate   memset(&sVac, 0, sizeof(sVac));
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate   /* Get the full pathname of the database file and create two
2487c478bd9Sstevel@tonic-gate   ** temporary filenames in the same directory as the original file.
2497c478bd9Sstevel@tonic-gate   */
2507c478bd9Sstevel@tonic-gate   zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
2517c478bd9Sstevel@tonic-gate   if( zFilename==0 ){
2527c478bd9Sstevel@tonic-gate     /* This only happens with the in-memory database.  VACUUM is a no-op
2537c478bd9Sstevel@tonic-gate     ** there, so just return */
2547c478bd9Sstevel@tonic-gate     return SQLITE_OK;
2557c478bd9Sstevel@tonic-gate   }
2567c478bd9Sstevel@tonic-gate   nFilename = strlen(zFilename);
2577c478bd9Sstevel@tonic-gate   zTemp = sqliteMalloc( nFilename+100 );
2587c478bd9Sstevel@tonic-gate   if( zTemp==0 ) return SQLITE_NOMEM;
2597c478bd9Sstevel@tonic-gate   strcpy(zTemp, zFilename);
2607c478bd9Sstevel@tonic-gate   for(i=0; i<10; i++){
2617c478bd9Sstevel@tonic-gate     zTemp[nFilename] = '-';
2627c478bd9Sstevel@tonic-gate     randomName((unsigned char*)&zTemp[nFilename+1]);
2637c478bd9Sstevel@tonic-gate     if( !sqliteOsFileExists(zTemp) ) break;
2647c478bd9Sstevel@tonic-gate   }
2657c478bd9Sstevel@tonic-gate   if( i>=10 ){
2667c478bd9Sstevel@tonic-gate     sqliteSetString(pzErrMsg, "unable to create a temporary database file "
2677c478bd9Sstevel@tonic-gate        "in the same directory as the original database", (char*)0);
2687c478bd9Sstevel@tonic-gate     goto end_of_vacuum;
2697c478bd9Sstevel@tonic-gate   }
2707c478bd9Sstevel@tonic-gate 
271*1da57d55SToomas Soome 
2727c478bd9Sstevel@tonic-gate   dbNew = sqlite_open(zTemp, 0, &zErrMsg);
2737c478bd9Sstevel@tonic-gate   if( dbNew==0 ){
2747c478bd9Sstevel@tonic-gate     sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
2757c478bd9Sstevel@tonic-gate        zTemp, " - ", zErrMsg, (char*)0);
2767c478bd9Sstevel@tonic-gate     goto end_of_vacuum;
2777c478bd9Sstevel@tonic-gate   }
2787c478bd9Sstevel@tonic-gate   if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
2797c478bd9Sstevel@tonic-gate   if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
2807c478bd9Sstevel@tonic-gate     goto end_of_vacuum;
2817c478bd9Sstevel@tonic-gate   }
282*1da57d55SToomas Soome 
2837c478bd9Sstevel@tonic-gate   sVac.dbOld = db;
2847c478bd9Sstevel@tonic-gate   sVac.dbNew = dbNew;
2857c478bd9Sstevel@tonic-gate   sVac.pzErrMsg = pzErrMsg;
2867c478bd9Sstevel@tonic-gate   for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
2877c478bd9Sstevel@tonic-gate     char zBuf[200];
2887c478bd9Sstevel@tonic-gate     assert( strlen(zPragma[i])<100 );
2897c478bd9Sstevel@tonic-gate     sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
2907c478bd9Sstevel@tonic-gate     sVac.zPragma = zPragma[i];
2917c478bd9Sstevel@tonic-gate     rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
2927c478bd9Sstevel@tonic-gate   }
2937c478bd9Sstevel@tonic-gate   if( rc==SQLITE_OK ){
294*1da57d55SToomas Soome     rc = sqlite_exec(db,
2957c478bd9Sstevel@tonic-gate       "SELECT type, name, sql FROM sqlite_master "
2967c478bd9Sstevel@tonic-gate       "WHERE sql NOT NULL AND type!='view' "
2977c478bd9Sstevel@tonic-gate       "UNION ALL "
2987c478bd9Sstevel@tonic-gate       "SELECT type, name, sql FROM sqlite_master "
2997c478bd9Sstevel@tonic-gate       "WHERE sql NOT NULL AND type=='view'",
3007c478bd9Sstevel@tonic-gate       vacuumCallback1, &sVac, &zErrMsg);
3017c478bd9Sstevel@tonic-gate   }
3027c478bd9Sstevel@tonic-gate   if( rc==SQLITE_OK ){
3037c478bd9Sstevel@tonic-gate     rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
3047c478bd9Sstevel@tonic-gate     sqlite_exec(db, "COMMIT", 0, 0, 0);
3057c478bd9Sstevel@tonic-gate     sqliteResetInternalSchema(db, 0);
3067c478bd9Sstevel@tonic-gate   }
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate end_of_vacuum:
3097c478bd9Sstevel@tonic-gate   if( rc && zErrMsg!=0 ){
310*1da57d55SToomas Soome     sqliteSetString(pzErrMsg, "unable to vacuum database - ",
3117c478bd9Sstevel@tonic-gate        zErrMsg, (char*)0);
3127c478bd9Sstevel@tonic-gate   }
3137c478bd9Sstevel@tonic-gate   sqlite_exec(db, "ROLLBACK", 0, 0, 0);
314*1da57d55SToomas Soome   if( (dbNew && (dbNew->flags & SQLITE_Interrupt))
3157c478bd9Sstevel@tonic-gate          || (db->flags & SQLITE_Interrupt) ){
3167c478bd9Sstevel@tonic-gate     rc = SQLITE_INTERRUPT;
3177c478bd9Sstevel@tonic-gate   }
3187c478bd9Sstevel@tonic-gate   if( dbNew ) sqlite_close(dbNew);
3197c478bd9Sstevel@tonic-gate   sqliteOsDelete(zTemp);
3207c478bd9Sstevel@tonic-gate   sqliteFree(zTemp);
3217c478bd9Sstevel@tonic-gate   sqliteFree(sVac.s1.z);
3227c478bd9Sstevel@tonic-gate   sqliteFree(sVac.s2.z);
3237c478bd9Sstevel@tonic-gate   if( zErrMsg ) sqlite_freemem(zErrMsg);
3247c478bd9Sstevel@tonic-gate   if( rc==SQLITE_ABORT && sVac.rc!=SQLITE_INTERRUPT ) sVac.rc = SQLITE_ERROR;
3257c478bd9Sstevel@tonic-gate   return sVac.rc;
3267c478bd9Sstevel@tonic-gate #endif
3277c478bd9Sstevel@tonic-gate }
328