|
ds@1952
|
1 |
/* |
|
ds@1952
|
2 |
** 2003 April 6 |
|
ds@1952
|
3 |
** |
|
ds@1952
|
4 |
** The author disclaims copyright to this source code. In place of |
|
ds@1952
|
5 |
** a legal notice, here is a blessing: |
|
ds@1952
|
6 |
** |
|
ds@1952
|
7 |
** May you do good and not evil. |
|
ds@1952
|
8 |
** May you find forgiveness for yourself and forgive others. |
|
ds@1952
|
9 |
** May you share freely, never taking more than you give. |
|
ds@1952
|
10 |
** |
|
ds@1952
|
11 |
************************************************************************* |
|
ds@1952
|
12 |
** This file contains code used to implement the VACUUM command. |
|
ds@1952
|
13 |
** |
|
ds@1952
|
14 |
** Most of the code in this file may be omitted by defining the |
|
ds@1952
|
15 |
** SQLITE_OMIT_VACUUM macro. |
|
ds@1952
|
16 |
** |
|
ds@1952
|
17 |
** $Id$ |
|
ds@1952
|
18 |
*/ |
|
ds@1952
|
19 |
#include "sqliteInt.h" |
|
ds@1952
|
20 |
#include "vdbeInt.h" |
|
ds@1952
|
21 |
|
|
ds@1952
|
22 |
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) |
|
ds@1952
|
23 |
/* |
|
ds@1952
|
24 |
** Execute zSql on database db. Return an error code. |
|
ds@1952
|
25 |
*/ |
|
ds@1952
|
26 |
static int execSql(sqlite3 *db, const char *zSql){ |
|
ds@1952
|
27 |
sqlite3_stmt *pStmt; |
|
ds@1952
|
28 |
if( !zSql ){ |
|
ds@1952
|
29 |
return SQLITE_NOMEM; |
|
ds@1952
|
30 |
} |
|
ds@1952
|
31 |
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ |
|
ds@1952
|
32 |
return sqlite3_errcode(db); |
|
ds@1952
|
33 |
} |
|
ds@1952
|
34 |
while( SQLITE_ROW==sqlite3_step(pStmt) ){} |
|
ds@1952
|
35 |
return sqlite3_finalize(pStmt); |
|
ds@1952
|
36 |
} |
|
ds@1952
|
37 |
|
|
ds@1952
|
38 |
/* |
|
ds@1952
|
39 |
** Execute zSql on database db. The statement returns exactly |
|
ds@1952
|
40 |
** one column. Execute this as SQL on the same database. |
|
ds@1952
|
41 |
*/ |
|
ds@1952
|
42 |
static int execExecSql(sqlite3 *db, const char *zSql){ |
|
ds@1952
|
43 |
sqlite3_stmt *pStmt; |
|
ds@1952
|
44 |
int rc; |
|
ds@1952
|
45 |
|
|
ds@1952
|
46 |
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); |
|
ds@1952
|
47 |
if( rc!=SQLITE_OK ) return rc; |
|
ds@1952
|
48 |
|
|
ds@1952
|
49 |
while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
|
ds@1952
|
50 |
rc = execSql(db, (char*)sqlite3_column_text(pStmt, 0)); |
|
ds@1952
|
51 |
if( rc!=SQLITE_OK ){ |
|
ds@1952
|
52 |
sqlite3_finalize(pStmt); |
|
ds@1952
|
53 |
return rc; |
|
ds@1952
|
54 |
} |
|
ds@1952
|
55 |
} |
|
ds@1952
|
56 |
|
|
ds@1952
|
57 |
return sqlite3_finalize(pStmt); |
|
ds@1952
|
58 |
} |
|
ds@1952
|
59 |
|
|
ds@1952
|
60 |
/* |
|
ds@1952
|
61 |
** The non-standard VACUUM command is used to clean up the database, |
|
ds@1952
|
62 |
** collapse free space, etc. It is modelled after the VACUUM command |
|
ds@1952
|
63 |
** in PostgreSQL. |
|
ds@1952
|
64 |
** |
|
ds@1952
|
65 |
** In version 1.0.x of SQLite, the VACUUM command would call |
|
ds@1952
|
66 |
** gdbm_reorganize() on all the database tables. But beginning |
|
ds@1952
|
67 |
** with 2.0.0, SQLite no longer uses GDBM so this command has |
|
ds@1952
|
68 |
** become a no-op. |
|
ds@1952
|
69 |
*/ |
|
ds@1952
|
70 |
void sqlite3Vacuum(Parse *pParse){ |
|
ds@1952
|
71 |
Vdbe *v = sqlite3GetVdbe(pParse); |
|
ds@1952
|
72 |
if( v ){ |
|
ds@1952
|
73 |
sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0); |
|
ds@1952
|
74 |
} |
|
ds@1952
|
75 |
return; |
|
ds@1952
|
76 |
} |
|
ds@1952
|
77 |
|
|
ds@1952
|
78 |
/* |
|
ds@1952
|
79 |
** This routine implements the OP_Vacuum opcode of the VDBE. |
|
ds@1952
|
80 |
*/ |
|
ds@1952
|
81 |
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
|
ds@1952
|
82 |
int rc = SQLITE_OK; /* Return code from service routines */ |
|
ds@1952
|
83 |
Btree *pMain; /* The database being vacuumed */ |
|
ds@1952
|
84 |
Btree *pTemp; /* The temporary database we vacuum into */ |
|
ds@1952
|
85 |
char *zSql = 0; /* SQL statements */ |
|
ds@1952
|
86 |
int saved_flags; /* Saved value of the db->flags */ |
|
ds@1952
|
87 |
Db *pDb = 0; /* Database to detach at end of vacuum */ |
|
ds@1952
|
88 |
|
|
ds@1952
|
89 |
/* Save the current value of the write-schema flag before setting it. */ |
|
ds@1952
|
90 |
saved_flags = db->flags; |
|
ds@1952
|
91 |
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; |
|
ds@1952
|
92 |
|
|
ds@1952
|
93 |
if( !db->autoCommit ){ |
|
ds@1952
|
94 |
sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", |
|
ds@1952
|
95 |
(char*)0); |
|
ds@1952
|
96 |
rc = SQLITE_ERROR; |
|
ds@1952
|
97 |
goto end_of_vacuum; |
|
ds@1952
|
98 |
} |
|
ds@1952
|
99 |
pMain = db->aDb[0].pBt; |
|
ds@1952
|
100 |
|
|
ds@1952
|
101 |
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma |
|
ds@1952
|
102 |
** can be set to 'off' for this file, as it is not recovered if a crash |
|
ds@1952
|
103 |
** occurs anyway. The integrity of the database is maintained by a |
|
ds@1952
|
104 |
** (possibly synchronous) transaction opened on the main database before |
|
ds@1952
|
105 |
** sqlite3BtreeCopyFile() is called. |
|
ds@1952
|
106 |
** |
|
ds@1952
|
107 |
** An optimisation would be to use a non-journaled pager. |
|
ds@1952
|
108 |
*/ |
|
ds@1952
|
109 |
zSql = "ATTACH '' AS vacuum_db;"; |
|
ds@1952
|
110 |
rc = execSql(db, zSql); |
|
ds@1952
|
111 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
112 |
pDb = &db->aDb[db->nDb-1]; |
|
ds@1952
|
113 |
assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); |
|
ds@1952
|
114 |
pTemp = db->aDb[db->nDb-1].pBt; |
|
ds@1952
|
115 |
sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), |
|
ds@1952
|
116 |
sqlite3BtreeGetReserve(pMain)); |
|
ds@1952
|
117 |
if( db->mallocFailed ){ |
|
ds@1952
|
118 |
rc = SQLITE_NOMEM; |
|
ds@1952
|
119 |
goto end_of_vacuum; |
|
ds@1952
|
120 |
} |
|
ds@1952
|
121 |
assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) ); |
|
ds@1952
|
122 |
rc = execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); |
|
ds@1952
|
123 |
if( rc!=SQLITE_OK ){ |
|
ds@1952
|
124 |
goto end_of_vacuum; |
|
ds@1952
|
125 |
} |
|
ds@1952
|
126 |
|
|
ds@1952
|
127 |
#ifndef SQLITE_OMIT_AUTOVACUUM |
|
ds@1952
|
128 |
sqlite3BtreeSetAutoVacuum(pTemp, sqlite3BtreeGetAutoVacuum(pMain)); |
|
ds@1952
|
129 |
#endif |
|
ds@1952
|
130 |
|
|
ds@1952
|
131 |
/* Begin a transaction */ |
|
ds@1952
|
132 |
rc = execSql(db, "BEGIN EXCLUSIVE;"); |
|
ds@1952
|
133 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
134 |
|
|
ds@1952
|
135 |
/* Query the schema of the main database. Create a mirror schema |
|
ds@1952
|
136 |
** in the temporary database. |
|
ds@1952
|
137 |
*/ |
|
ds@1952
|
138 |
rc = execExecSql(db, |
|
ds@1952
|
139 |
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " |
|
ds@1952
|
140 |
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" |
|
ds@1952
|
141 |
" AND rootpage>0" |
|
ds@1952
|
142 |
); |
|
ds@1952
|
143 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
144 |
rc = execExecSql(db, |
|
ds@1952
|
145 |
"SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000)" |
|
ds@1952
|
146 |
" FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "); |
|
ds@1952
|
147 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
148 |
rc = execExecSql(db, |
|
ds@1952
|
149 |
"SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) " |
|
ds@1952
|
150 |
" FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"); |
|
ds@1952
|
151 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
152 |
|
|
ds@1952
|
153 |
/* Loop through the tables in the main database. For each, do |
|
ds@1952
|
154 |
** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy |
|
ds@1952
|
155 |
** the contents to the temporary database. |
|
ds@1952
|
156 |
*/ |
|
ds@1952
|
157 |
rc = execExecSql(db, |
|
ds@1952
|
158 |
"SELECT 'INSERT INTO vacuum_db.' || quote(name) " |
|
ds@1952
|
159 |
"|| ' SELECT * FROM ' || quote(name) || ';'" |
|
ds@1952
|
160 |
"FROM sqlite_master " |
|
ds@1952
|
161 |
"WHERE type = 'table' AND name!='sqlite_sequence' " |
|
ds@1952
|
162 |
" AND rootpage>0" |
|
ds@1952
|
163 |
|
|
ds@1952
|
164 |
); |
|
ds@1952
|
165 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
166 |
|
|
ds@1952
|
167 |
/* Copy over the sequence table |
|
ds@1952
|
168 |
*/ |
|
ds@1952
|
169 |
rc = execExecSql(db, |
|
ds@1952
|
170 |
"SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' " |
|
ds@1952
|
171 |
"FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' " |
|
ds@1952
|
172 |
); |
|
ds@1952
|
173 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
174 |
rc = execExecSql(db, |
|
ds@1952
|
175 |
"SELECT 'INSERT INTO vacuum_db.' || quote(name) " |
|
ds@1952
|
176 |
"|| ' SELECT * FROM ' || quote(name) || ';' " |
|
ds@1952
|
177 |
"FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" |
|
ds@1952
|
178 |
); |
|
ds@1952
|
179 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
180 |
|
|
ds@1952
|
181 |
|
|
ds@1952
|
182 |
/* Copy the triggers, views, and virtual tables from the main database |
|
ds@1952
|
183 |
** over to the temporary database. None of these objects has any |
|
ds@1952
|
184 |
** associated storage, so all we have to do is copy their entries |
|
ds@1952
|
185 |
** from the SQLITE_MASTER table. |
|
ds@1952
|
186 |
*/ |
|
ds@1952
|
187 |
rc = execSql(db, |
|
ds@1952
|
188 |
"INSERT INTO vacuum_db.sqlite_master " |
|
ds@1952
|
189 |
" SELECT type, name, tbl_name, rootpage, sql" |
|
ds@1952
|
190 |
" FROM sqlite_master" |
|
ds@1952
|
191 |
" WHERE type='view' OR type='trigger'" |
|
ds@1952
|
192 |
" OR (type='table' AND rootpage=0)" |
|
ds@1952
|
193 |
); |
|
ds@1952
|
194 |
if( rc ) goto end_of_vacuum; |
|
ds@1952
|
195 |
|
|
ds@1952
|
196 |
/* At this point, unless the main db was completely empty, there is now a |
|
ds@1952
|
197 |
** transaction open on the vacuum database, but not on the main database. |
|
ds@1952
|
198 |
** Open a btree level transaction on the main database. This allows a |
|
ds@1952
|
199 |
** call to sqlite3BtreeCopyFile(). The main database btree level |
|
ds@1952
|
200 |
** transaction is then committed, so the SQL level never knows it was |
|
ds@1952
|
201 |
** opened for writing. This way, the SQL transaction used to create the |
|
ds@1952
|
202 |
** temporary database never needs to be committed. |
|
ds@1952
|
203 |
*/ |
|
ds@1952
|
204 |
if( rc==SQLITE_OK ){ |
|
ds@1952
|
205 |
u32 meta; |
|
ds@1952
|
206 |
int i; |
|
ds@1952
|
207 |
|
|
ds@1952
|
208 |
/* This array determines which meta meta values are preserved in the |
|
ds@1952
|
209 |
** vacuum. Even entries are the meta value number and odd entries |
|
ds@1952
|
210 |
** are an increment to apply to the meta value after the vacuum. |
|
ds@1952
|
211 |
** The increment is used to increase the schema cookie so that other |
|
ds@1952
|
212 |
** connections to the same database will know to reread the schema. |
|
ds@1952
|
213 |
*/ |
|
ds@1952
|
214 |
static const unsigned char aCopy[] = { |
|
ds@1952
|
215 |
1, 1, /* Add one to the old schema cookie */ |
|
ds@1952
|
216 |
3, 0, /* Preserve the default page cache size */ |
|
ds@1952
|
217 |
5, 0, /* Preserve the default text encoding */ |
|
ds@1952
|
218 |
6, 0, /* Preserve the user version */ |
|
ds@1952
|
219 |
}; |
|
ds@1952
|
220 |
|
|
ds@1952
|
221 |
assert( 1==sqlite3BtreeIsInTrans(pTemp) ); |
|
ds@1952
|
222 |
assert( 1==sqlite3BtreeIsInTrans(pMain) ); |
|
ds@1952
|
223 |
|
|
ds@1952
|
224 |
/* Copy Btree meta values */ |
|
ds@1952
|
225 |
for(i=0; i<sizeof(aCopy)/sizeof(aCopy[0]); i+=2){ |
|
ds@1952
|
226 |
rc = sqlite3BtreeGetMeta(pMain, aCopy[i], &meta); |
|
ds@1952
|
227 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
228 |
rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]); |
|
ds@1952
|
229 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
230 |
} |
|
ds@1952
|
231 |
|
|
ds@1952
|
232 |
rc = sqlite3BtreeCopyFile(pMain, pTemp); |
|
ds@1952
|
233 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
234 |
rc = sqlite3BtreeCommit(pTemp); |
|
ds@1952
|
235 |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
|
ds@1952
|
236 |
rc = sqlite3BtreeCommit(pMain); |
|
ds@1952
|
237 |
} |
|
ds@1952
|
238 |
|
|
ds@1952
|
239 |
end_of_vacuum: |
|
ds@1952
|
240 |
/* Restore the original value of db->flags */ |
|
ds@1952
|
241 |
db->flags = saved_flags; |
|
ds@1952
|
242 |
|
|
ds@1952
|
243 |
/* Currently there is an SQL level transaction open on the vacuum |
|
ds@1952
|
244 |
** database. No locks are held on any other files (since the main file |
|
ds@1952
|
245 |
** was committed at the btree level). So it safe to end the transaction |
|
ds@1952
|
246 |
** by manually setting the autoCommit flag to true and detaching the |
|
ds@1952
|
247 |
** vacuum database. The vacuum_db journal file is deleted when the pager |
|
ds@1952
|
248 |
** is closed by the DETACH. |
|
ds@1952
|
249 |
*/ |
|
ds@1952
|
250 |
db->autoCommit = 1; |
|
ds@1952
|
251 |
|
|
ds@1952
|
252 |
if( pDb ){ |
|
ds@1952
|
253 |
sqlite3BtreeClose(pDb->pBt); |
|
ds@1952
|
254 |
pDb->pBt = 0; |
|
ds@1952
|
255 |
pDb->pSchema = 0; |
|
ds@1952
|
256 |
} |
|
ds@1952
|
257 |
|
|
ds@1952
|
258 |
sqlite3ResetInternalSchema(db, 0); |
|
ds@1952
|
259 |
|
|
ds@1952
|
260 |
return rc; |
|
ds@1952
|
261 |
} |
|
ds@1952
|
262 |
#endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ |