diff options
Diffstat (limited to '')
-rw-r--r-- | ext/rbu/rbu.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/ext/rbu/rbu.c b/ext/rbu/rbu.c new file mode 100644 index 0000000..7a24db2 --- /dev/null +++ b/ext/rbu/rbu.c @@ -0,0 +1,195 @@ +/* +** 2014 August 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains a command-line application that uses the RBU +** extension. See the usage() function below for an explanation. +*/ + +#include "sqlite3rbu.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* +** Print a usage message and exit. +*/ +void usage(const char *zArgv0){ + fprintf(stderr, +"Usage: %s ?OPTIONS? TARGET-DB RBU-DB\n" +"\n" +"Where options are:\n" +"\n" +" -step NSTEP\n" +" -statstep NSTATSTEP\n" +" -vacuum\n" +" -presql SQL\n" +"\n" +" If the -vacuum switch is not present, argument RBU-DB must be an RBU\n" +" database containing an update suitable for target database TARGET-DB.\n" +" Or, if -vacuum is specified, then TARGET-DB is a database to vacuum using\n" +" RBU, and RBU-DB is used as the state database for the vacuum (refer to\n" +" API documentation for details).\n" +"\n" +" If NSTEP is set to less than or equal to zero (the default value), this \n" +" program attempts to perform the entire update or vacuum operation before\n" +" exiting\n" +"\n" +" If NSTEP is greater than zero, then a maximum of NSTEP calls are made\n" +" to sqlite3rbu_step(). If the RBU update has not been completely applied\n" +" after the NSTEP'th call is made, the state is saved in the database RBU-DB\n" +" and the program exits. Subsequent invocations of this (or any other RBU)\n" +" application will use this state to resume applying the RBU update to the\n" +" target db.\n" +"\n" +, zArgv0); + exit(1); +} + +void report_default_vfs(){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); + fprintf(stdout, "default vfs is \"%s\"\n", pVfs ? pVfs->zName : "NULL"); +} + +void report_rbu_vfs(sqlite3rbu *pRbu){ + sqlite3 *db = sqlite3rbu_db(pRbu, 0); + if( db ){ + char *zName = 0; + sqlite3_file_control(db, "main", SQLITE_FCNTL_VFSNAME, &zName); + if( zName ){ + fprintf(stdout, "using vfs \"%s\"\n", zName); + }else{ + fprintf(stdout, "vfs name not available\n"); + } + sqlite3_free(zName); + } +} + +int main(int argc, char **argv){ + int i; + const char *zTarget; /* Target database to apply RBU to */ + const char *zRbu; /* Database containing RBU */ + char zBuf[200]; /* Buffer for printf() */ + char *zErrmsg = 0; /* Error message, if any */ + sqlite3rbu *pRbu; /* RBU handle */ + int nStep = 0; /* Maximum number of step() calls */ + int nStatStep = 0; /* Report stats after this many step calls */ + int bVacuum = 0; + const char *zPreSql = 0; + int rc = SQLITE_OK; + sqlite3_int64 nProgress = 0; + int nArgc = argc-2; + + if( argc<3 ) usage(argv[0]); + for(i=1; i<nArgc; i++){ + const char *zArg = argv[i]; + int nArg = strlen(zArg); + if( nArg>1 && nArg<=8 && 0==memcmp(zArg, "-vacuum", nArg) ){ + bVacuum = 1; + }else if( nArg>1 && nArg<=7 + && 0==memcmp(zArg, "-presql", nArg) && i<nArg-1 ){ + i++; + zPreSql = argv[i]; + }else if( nArg>1 && nArg<=5 && 0==memcmp(zArg, "-step", nArg) && i<nArg-1 ){ + i++; + nStep = atoi(argv[i]); + }else if( nArg>1 && nArg<=9 + && 0==memcmp(zArg, "-statstep", nArg) && i<nArg-1 + ){ + i++; + nStatStep = atoi(argv[i]); + }else{ + usage(argv[0]); + } + } + + zTarget = argv[argc-2]; + zRbu = argv[argc-1]; + + report_default_vfs(); + + /* Open an RBU handle. A vacuum handle if -vacuum was specified, or a + ** regular RBU update handle otherwise. */ + if( bVacuum ){ + pRbu = sqlite3rbu_vacuum(zTarget, zRbu); + }else{ + pRbu = sqlite3rbu_open(zTarget, zRbu, 0); + } + report_rbu_vfs(pRbu); + + if( zPreSql && pRbu ){ + sqlite3 *dbMain = sqlite3rbu_db(pRbu, 0); + rc = sqlite3_exec(dbMain, zPreSql, 0, 0, 0); + if( rc==SQLITE_OK ){ + sqlite3 *dbRbu = sqlite3rbu_db(pRbu, 1); + rc = sqlite3_exec(dbRbu, zPreSql, 0, 0, 0); + } + } + + /* If nStep is less than or equal to zero, call + ** sqlite3rbu_step() until either the RBU has been completely applied + ** or an error occurs. Or, if nStep is greater than zero, call + ** sqlite3rbu_step() a maximum of nStep times. */ + if( rc==SQLITE_OK ){ + for(i=0; (nStep<=0 || i<nStep) && sqlite3rbu_step(pRbu)==SQLITE_OK; i++){ + if( nStatStep>0 && (i % nStatStep)==0 ){ + sqlite3_int64 nUsed; + sqlite3_int64 nHighwater; + sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0); + fprintf(stdout, "memory used=%lld highwater=%lld", nUsed, nHighwater); + if( bVacuum==0 ){ + int one; + int two; + sqlite3rbu_bp_progress(pRbu, &one, &two); + fprintf(stdout, " progress=%d/%d\n", one, two); + }else{ + fprintf(stdout, "\n"); + } + fflush(stdout); + } + } + nProgress = sqlite3rbu_progress(pRbu); + rc = sqlite3rbu_close(pRbu, &zErrmsg); + } + + /* Let the user know what happened. */ + switch( rc ){ + case SQLITE_OK: + sqlite3_snprintf(sizeof(zBuf), zBuf, + "SQLITE_OK: rbu update incomplete (%lld operations so far)\n", + nProgress + ); + fprintf(stdout, "%s", zBuf); + break; + + case SQLITE_DONE: + sqlite3_snprintf(sizeof(zBuf), zBuf, + "SQLITE_DONE: rbu update completed (%lld operations)\n", + nProgress + ); + fprintf(stdout, "%s", zBuf); + break; + + default: + fprintf(stderr, "error=%d: %s\n", rc, zErrmsg); + break; + } + + if( nStatStep>0 ){ + sqlite3_int64 nUsed; + sqlite3_int64 nHighwater; + sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0); + fprintf(stdout, "memory used=%lld highwater=%lld\n", nUsed, nHighwater); + } + + sqlite3_free(zErrmsg); + return (rc==SQLITE_OK || rc==SQLITE_DONE) ? 0 : 1; +} |