Annotation of embedaddon/php/ext/sqlite/libsqlite/src/vacuum.c, revision 1.1.1.1

1.1       misho       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 195361 2005-09-07 15:11:33Z iliaa $
                     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: */
                     26: typedef struct dynStr dynStr;
                     27: struct 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: */
                     36: typedef struct vacuumStruct vacuumStruct;
                     37: struct 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: */
                     51: static 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: */
                     71: static 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: */
                     91: static 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: */
                    109: static 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: */
                    141: static 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: */
                    169: static 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: */
                    191: void 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: */
                    200: int 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: 
                    286: end_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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>