Annotation of embedaddon/php/ext/sqlite/libsqlite/src/vacuum.c, revision 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>