00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "sqliteInt.h"
00020 #include "os.h"
00021
00022
00023
00024
00025
00026 typedef struct dynStr dynStr;
00027 struct dynStr {
00028 char *z;
00029 int nAlloc;
00030 int nUsed;
00031 };
00032
00033
00034
00035
00036 typedef struct vacuumStruct vacuumStruct;
00037 struct vacuumStruct {
00038 sqlite *dbOld;
00039 sqlite *dbNew;
00040 char **pzErrMsg;
00041 int rc;
00042 const char *zTable;
00043 const char *zPragma;
00044 dynStr s1, s2;
00045 };
00046
00047 #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
00048
00049
00050
00051 static void appendText(dynStr *p, const char *zText, int nText){
00052 if( nText<0 ) nText = strlen(zText);
00053 if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
00054 char *zNew;
00055 p->nAlloc = p->nUsed + nText + 1000;
00056 zNew = sqliteRealloc(p->z, p->nAlloc);
00057 if( zNew==0 ){
00058 sqliteFree(p->z);
00059 memset(p, 0, sizeof(*p));
00060 return;
00061 }
00062 p->z = zNew;
00063 }
00064 memcpy(&p->z[p->nUsed], zText, nText+1);
00065 p->nUsed += nText;
00066 }
00067
00068
00069
00070
00071 static void appendQuoted(dynStr *p, const char *zText){
00072 int i, j;
00073 appendText(p, "'", 1);
00074 for(i=j=0; zText[i]; i++){
00075 if( zText[i]=='\'' ){
00076 appendText(p, &zText[j], i-j+1);
00077 j = i + 1;
00078 appendText(p, "'", 1);
00079 }
00080 }
00081 if( j<i ){
00082 appendText(p, &zText[j], i-j);
00083 }
00084 appendText(p, "'", 1);
00085 }
00086
00087
00088
00089
00090
00091 static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
00092 char *zErrMsg = 0;
00093 int rc;
00094
00095
00096 rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
00097 if( zErrMsg ){
00098 sqliteSetString(pzErrMsg, zErrMsg, (char*)0);
00099 sqlite_freemem(zErrMsg);
00100 }
00101 return rc;
00102 }
00103
00104
00105
00106
00107
00108
00109 static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
00110 vacuumStruct *p = (vacuumStruct*)pArg;
00111 const char *zSep = "(";
00112 int i;
00113
00114 if( argv==0 ) return 0;
00115 p->s2.nUsed = 0;
00116 appendText(&p->s2, "INSERT INTO ", -1);
00117 appendQuoted(&p->s2, p->zTable);
00118 appendText(&p->s2, " VALUES", -1);
00119 for(i=0; i<argc; i++){
00120 appendText(&p->s2, zSep, 1);
00121 zSep = ",";
00122 if( argv[i]==0 ){
00123 appendText(&p->s2, "NULL", 4);
00124 }else{
00125 appendQuoted(&p->s2, argv[i]);
00126 }
00127 }
00128 appendText(&p->s2,")", 1);
00129 p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
00130 return p->rc;
00131 }
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141 static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
00142 vacuumStruct *p = (vacuumStruct*)pArg;
00143 int rc = 0;
00144 assert( argc==3 );
00145 if( argv==0 ) return 0;
00146 assert( argv[0]!=0 );
00147 assert( argv[1]!=0 );
00148 assert( argv[2]!=0 );
00149 rc = execsql(p->pzErrMsg, p->dbNew, argv[2]);
00150 if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
00151 char *zErrMsg = 0;
00152 p->s1.nUsed = 0;
00153 appendText(&p->s1, "SELECT * FROM ", -1);
00154 appendQuoted(&p->s1, argv[1]);
00155 p->zTable = argv[1];
00156 rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
00157 if( zErrMsg ){
00158 sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
00159 sqlite_freemem(zErrMsg);
00160 }
00161 }
00162 if( rc!=SQLITE_ABORT ) p->rc = rc;
00163 return rc;
00164 }
00165
00166
00167
00168
00169 static void randomName(unsigned char *zBuf){
00170 static const unsigned char zChars[] =
00171 "abcdefghijklmnopqrstuvwxyz"
00172 "0123456789";
00173 int i;
00174 sqliteRandomness(20, zBuf);
00175 for(i=0; i<20; i++){
00176 zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
00177 }
00178 }
00179 #endif
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 void sqliteVacuum(Parse *pParse, Token *pTableName){
00192 Vdbe *v = sqliteGetVdbe(pParse);
00193 sqliteVdbeAddOp(v, OP_Vacuum, 0, 0);
00194 return;
00195 }
00196
00197
00198
00199
00200 int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
00201 #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
00202 const char *zFilename;
00203 int nFilename;
00204 char *zTemp = 0;
00205 sqlite *dbNew = 0;
00206 int rc = SQLITE_OK;
00207 int i;
00208 char *zErrMsg;
00209 vacuumStruct sVac;
00210
00211 if( db->flags & SQLITE_InTrans ){
00212 sqliteSetString(pzErrMsg, "cannot VACUUM from within a transaction",
00213 (char*)0);
00214 return SQLITE_ERROR;
00215 }
00216 if( db->flags & SQLITE_Interrupt ){
00217 return SQLITE_INTERRUPT;
00218 }
00219 memset(&sVac, 0, sizeof(sVac));
00220
00221
00222
00223
00224 zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
00225 if( zFilename==0 ){
00226
00227
00228 return SQLITE_OK;
00229 }
00230 nFilename = strlen(zFilename);
00231 zTemp = sqliteMalloc( nFilename+100 );
00232 if( zTemp==0 ) return SQLITE_NOMEM;
00233 strcpy(zTemp, zFilename);
00234 for(i=0; i<10; i++){
00235 zTemp[nFilename] = '-';
00236 randomName((unsigned char*)&zTemp[nFilename+1]);
00237 if( !sqliteOsFileExists(zTemp) ) break;
00238 }
00239 if( i>=10 ){
00240 sqliteSetString(pzErrMsg, "unable to create a temporary database file "
00241 "in the same directory as the original database", (char*)0);
00242 goto end_of_vacuum;
00243 }
00244
00245
00246 dbNew = sqlite_open(zTemp, 0, &zErrMsg);
00247 if( dbNew==0 ){
00248 sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
00249 zTemp, " - ", zErrMsg, (char*)0);
00250 goto end_of_vacuum;
00251 }
00252 if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
00253 if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
00254 goto end_of_vacuum;
00255 }
00256
00257 sVac.dbOld = db;
00258 sVac.dbNew = dbNew;
00259 sVac.pzErrMsg = pzErrMsg;
00260 if( rc==SQLITE_OK ){
00261 rc = sqlite_exec(db,
00262 "SELECT type, name, sql FROM sqlite_master "
00263 "WHERE sql NOT NULL AND type!='view' "
00264 "UNION ALL "
00265 "SELECT type, name, sql FROM sqlite_master "
00266 "WHERE sql NOT NULL AND type=='view'",
00267 vacuumCallback1, &sVac, &zErrMsg);
00268 }
00269 if( rc==SQLITE_OK ){
00270 int meta1[SQLITE_N_BTREE_META];
00271 int meta2[SQLITE_N_BTREE_META];
00272 sqliteBtreeGetMeta(db->aDb[0].pBt, meta1);
00273 sqliteBtreeGetMeta(dbNew->aDb[0].pBt, meta2);
00274 meta2[1] = meta1[1]+1;
00275 meta2[3] = meta1[3];
00276 meta2[4] = meta1[4];
00277 meta2[6] = meta1[6];
00278 rc = sqliteBtreeUpdateMeta(dbNew->aDb[0].pBt, meta2);
00279 }
00280 if( rc==SQLITE_OK ){
00281 rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
00282 sqlite_exec(db, "COMMIT", 0, 0, 0);
00283 sqliteResetInternalSchema(db, 0);
00284 }
00285
00286 end_of_vacuum:
00287 if( rc && zErrMsg!=0 ){
00288 sqliteSetString(pzErrMsg, "unable to vacuum database - ",
00289 zErrMsg, (char*)0);
00290 }
00291 sqlite_exec(db, "ROLLBACK", 0, 0, 0);
00292 if( (dbNew && (dbNew->flags & SQLITE_Interrupt))
00293 || (db->flags & SQLITE_Interrupt) ){
00294 rc = SQLITE_INTERRUPT;
00295 }
00296 if( dbNew ) sqlite_close(dbNew);
00297 sqliteOsDelete(zTemp);
00298 sqliteFree(zTemp);
00299 sqliteFree(sVac.s1.z);
00300 sqliteFree(sVac.s2.z);
00301 if( zErrMsg ) sqlite_freemem(zErrMsg);
00302 if( rc==SQLITE_ABORT && sVac.rc!=SQLITE_INTERRUPT ) sVac.rc = SQLITE_ERROR;
00303 return sVac.rc;
00304 #endif
00305 }