source: trunk/src/3rdparty/sqlite/vacuum.c@ 208

Last change on this file since 208 was 205, checked in by rudi, 14 years ago

Added SQLite 2.8.17 sources. This allows to build at least one of the sql drivers / plugins.

File size: 9.0 KB
Line 
1/*
2** 2003 April 6
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11*************************************************************************
12** This file contains code used to implement the VACUUM command.
13**
14** Most of the code in this file may be omitted by defining the
15** SQLITE_OMIT_VACUUM macro.
16**
17** $Id: vacuum.c,v 1.13.2.5 2005/02/14 00:25:48 drh Exp $
18*/
19#include "sqliteInt.h"
20#include "os.h"
21
22/*
23** A structure for holding a dynamic string - a string that can grow
24** without bound.
25*/
26typedef struct dynStr dynStr;
27struct dynStr {
28 char *z; /* Text of the string in space obtained from sqliteMalloc() */
29 int nAlloc; /* Amount of space allocated to z[] */
30 int nUsed; /* Next unused slot in z[] */
31};
32
33/*
34** A structure that holds the vacuum context
35*/
36typedef struct vacuumStruct vacuumStruct;
37struct vacuumStruct {
38 sqlite *dbOld; /* Original database */
39 sqlite *dbNew; /* New database */
40 char **pzErrMsg; /* Write errors here */
41 int rc; /* Set to non-zero on an error */
42 const char *zTable; /* Name of a table being copied */
43 const char *zPragma; /* Pragma to execute with results */
44 dynStr s1, s2; /* Two dynamic strings */
45};
46
47#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
48/*
49** Append text to a dynamic string
50*/
51static void appendText(dynStr *p, const char *zText, int nText){
52 if( nText<0 ) nText = strlen(zText);
53 if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
54 char *zNew;
55 p->nAlloc = p->nUsed + nText + 1000;
56 zNew = sqliteRealloc(p->z, p->nAlloc);
57 if( zNew==0 ){
58 sqliteFree(p->z);
59 memset(p, 0, sizeof(*p));
60 return;
61 }
62 p->z = zNew;
63 }
64 memcpy(&p->z[p->nUsed], zText, nText+1);
65 p->nUsed += nText;
66}
67
68/*
69** Append text to a dynamic string, having first put the text in quotes.
70*/
71static void appendQuoted(dynStr *p, const char *zText){
72 int i, j;
73 appendText(p, "'", 1);
74 for(i=j=0; zText[i]; i++){
75 if( zText[i]=='\'' ){
76 appendText(p, &zText[j], i-j+1);
77 j = i + 1;
78 appendText(p, "'", 1);
79 }
80 }
81 if( j<i ){
82 appendText(p, &zText[j], i-j);
83 }
84 appendText(p, "'", 1);
85}
86
87/*
88** Execute statements of SQL. If an error occurs, write the error
89** message into *pzErrMsg and return non-zero.
90*/
91static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
92 char *zErrMsg = 0;
93 int rc;
94
95 /* printf("***** executing *****\n%s\n", zSql); */
96 rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
97 if( zErrMsg ){
98 sqliteSetString(pzErrMsg, zErrMsg, (char*)0);
99 sqlite_freemem(zErrMsg);
100 }
101 return rc;
102}
103
104/*
105** This is the second stage callback. Each invocation contains all the
106** data for a single row of a single table in the original database. This
107** routine must write that information into the new database.
108*/
109static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
110 vacuumStruct *p = (vacuumStruct*)pArg;
111 const char *zSep = "(";
112 int i;
113
114 if( argv==0 ) return 0;
115 p->s2.nUsed = 0;
116 appendText(&p->s2, "INSERT INTO ", -1);
117 appendQuoted(&p->s2, p->zTable);
118 appendText(&p->s2, " VALUES", -1);
119 for(i=0; i<argc; i++){
120 appendText(&p->s2, zSep, 1);
121 zSep = ",";
122 if( argv[i]==0 ){
123 appendText(&p->s2, "NULL", 4);
124 }else{
125 appendQuoted(&p->s2, argv[i]);
126 }
127 }
128 appendText(&p->s2,")", 1);
129 p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
130 return p->rc;
131}
132
133/*
134** This is the first stage callback. Each invocation contains three
135** arguments where are taken from the SQLITE_MASTER table of the original
136** database: (1) the entry type, (2) the entry name, and (3) the SQL for
137** the entry. In all cases, execute the SQL of the third argument.
138** For tables, run a query to select all entries in that table and
139** transfer them to the second-stage callback.
140*/
141static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
142 vacuumStruct *p = (vacuumStruct*)pArg;
143 int rc = 0;
144 assert( argc==3 );
145 if( argv==0 ) return 0;
146 assert( argv[0]!=0 );
147 assert( argv[1]!=0 );
148 assert( argv[2]!=0 );
149 rc = execsql(p->pzErrMsg, p->dbNew, argv[2]);
150 if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
151 char *zErrMsg = 0;
152 p->s1.nUsed = 0;
153 appendText(&p->s1, "SELECT * FROM ", -1);
154 appendQuoted(&p->s1, argv[1]);
155 p->zTable = argv[1];
156 rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
157 if( zErrMsg ){
158 sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
159 sqlite_freemem(zErrMsg);
160 }
161 }
162 if( rc!=SQLITE_ABORT ) p->rc = rc;
163 return rc;
164}
165
166/*
167** Generate a random name of 20 character in length.
168*/
169static void randomName(unsigned char *zBuf){
170 static const unsigned char zChars[] =
171 "abcdefghijklmnopqrstuvwxyz"
172 "0123456789";
173 int i;
174 sqliteRandomness(20, zBuf);
175 for(i=0; i<20; i++){
176 zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
177 }
178}
179#endif
180
181/*
182** The non-standard VACUUM command is used to clean up the database,
183** collapse free space, etc. It is modelled after the VACUUM command
184** in PostgreSQL.
185**
186** In version 1.0.x of SQLite, the VACUUM command would call
187** gdbm_reorganize() on all the database tables. But beginning
188** with 2.0.0, SQLite no longer uses GDBM so this command has
189** become a no-op.
190*/
191void sqliteVacuum(Parse *pParse, Token *pTableName){
192 Vdbe *v = sqliteGetVdbe(pParse);
193 sqliteVdbeAddOp(v, OP_Vacuum, 0, 0);
194 return;
195}
196
197/*
198** This routine implements the OP_Vacuum opcode of the VDBE.
199*/
200int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
201#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
202 const char *zFilename; /* full pathname of the database file */
203 int nFilename; /* number of characters in zFilename[] */
204 char *zTemp = 0; /* a temporary file in same directory as zFilename */
205 sqlite *dbNew = 0; /* The new vacuumed database */
206 int rc = SQLITE_OK; /* Return code from service routines */
207 int i; /* Loop counter */
208 char *zErrMsg; /* Error message */
209 vacuumStruct sVac; /* Information passed to callbacks */
210
211 if( db->flags & SQLITE_InTrans ){
212 sqliteSetString(pzErrMsg, "cannot VACUUM from within a transaction",
213 (char*)0);
214 return SQLITE_ERROR;
215 }
216 if( db->flags & SQLITE_Interrupt ){
217 return SQLITE_INTERRUPT;
218 }
219 memset(&sVac, 0, sizeof(sVac));
220
221 /* Get the full pathname of the database file and create two
222 ** temporary filenames in the same directory as the original file.
223 */
224 zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
225 if( zFilename==0 ){
226 /* This only happens with the in-memory database. VACUUM is a no-op
227 ** there, so just return */
228 return SQLITE_OK;
229 }
230 nFilename = strlen(zFilename);
231 zTemp = sqliteMalloc( nFilename+100 );
232 if( zTemp==0 ) return SQLITE_NOMEM;
233 strcpy(zTemp, zFilename);
234 for(i=0; i<10; i++){
235 zTemp[nFilename] = '-';
236 randomName((unsigned char*)&zTemp[nFilename+1]);
237 if( !sqliteOsFileExists(zTemp) ) break;
238 }
239 if( i>=10 ){
240 sqliteSetString(pzErrMsg, "unable to create a temporary database file "
241 "in the same directory as the original database", (char*)0);
242 goto end_of_vacuum;
243 }
244
245
246 dbNew = sqlite_open(zTemp, 0, &zErrMsg);
247 if( dbNew==0 ){
248 sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
249 zTemp, " - ", zErrMsg, (char*)0);
250 goto end_of_vacuum;
251 }
252 if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
253 if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
254 goto end_of_vacuum;
255 }
256
257 sVac.dbOld = db;
258 sVac.dbNew = dbNew;
259 sVac.pzErrMsg = pzErrMsg;
260 if( rc==SQLITE_OK ){
261 rc = sqlite_exec(db,
262 "SELECT type, name, sql FROM sqlite_master "
263 "WHERE sql NOT NULL AND type!='view' "
264 "UNION ALL "
265 "SELECT type, name, sql FROM sqlite_master "
266 "WHERE sql NOT NULL AND type=='view'",
267 vacuumCallback1, &sVac, &zErrMsg);
268 }
269 if( rc==SQLITE_OK ){
270 int meta1[SQLITE_N_BTREE_META];
271 int meta2[SQLITE_N_BTREE_META];
272 sqliteBtreeGetMeta(db->aDb[0].pBt, meta1);
273 sqliteBtreeGetMeta(dbNew->aDb[0].pBt, meta2);
274 meta2[1] = meta1[1]+1;
275 meta2[3] = meta1[3];
276 meta2[4] = meta1[4];
277 meta2[6] = meta1[6];
278 rc = sqliteBtreeUpdateMeta(dbNew->aDb[0].pBt, meta2);
279 }
280 if( rc==SQLITE_OK ){
281 rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
282 sqlite_exec(db, "COMMIT", 0, 0, 0);
283 sqliteResetInternalSchema(db, 0);
284 }
285
286end_of_vacuum:
287 if( rc && zErrMsg!=0 ){
288 sqliteSetString(pzErrMsg, "unable to vacuum database - ",
289 zErrMsg, (char*)0);
290 }
291 sqlite_exec(db, "ROLLBACK", 0, 0, 0);
292 if( (dbNew && (dbNew->flags & SQLITE_Interrupt))
293 || (db->flags & SQLITE_Interrupt) ){
294 rc = SQLITE_INTERRUPT;
295 }
296 if( dbNew ) sqlite_close(dbNew);
297 sqliteOsDelete(zTemp);
298 sqliteFree(zTemp);
299 sqliteFree(sVac.s1.z);
300 sqliteFree(sVac.s2.z);
301 if( zErrMsg ) sqlite_freemem(zErrMsg);
302 if( rc==SQLITE_ABORT && sVac.rc!=SQLITE_INTERRUPT ) sVac.rc = SQLITE_ERROR;
303 return sVac.rc;
304#endif
305}
Note: See TracBrowser for help on using the repository browser.