sqlite/ext/rbu/rbu.c
drh a959bf5311 Add NULL checks on all sqlite3_vfs_find(0) calls. This is not strictly
necessary.  There are no vulnerabilities here.  However, adding these
checks avoids unnecessary static analyzer complaints.
[forum:/forumpost/ce1193be15|Forum post ce1193be15].

FossilOrigin-Name: 272a15b9f418fb0b31a9808f7c42c20cf52318035ff98935d8e8519634357e8d
2021-06-15 15:15:40 +00:00

196 lines
5.9 KiB
C

/*
** 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;
}