From 3350ce95f7d754c8b8de51c926b870d06f9ee7ad Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 6 Feb 2014 00:49:12 +0000 Subject: [PATCH] Add the ".repair" command to the command-line shell. FossilOrigin-Name: d1dfadea87ecf18eeb6d2f21769deaa97473ca0e --- manifest | 14 ++-- manifest.uuid | 2 +- src/shell.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index da13307c6c..c624ecece4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sthe\sWin32\sVFS,\sthe\swinSysInfo\svariable\sshould\sbe\sstatic. -D 2014-02-05T11:05:47.687 +C Add\sthe\s".repair"\scommand\sto\sthe\scommand-line\sshell. +D 2014-02-06T00:49:12.328 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -220,7 +220,7 @@ F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c b78f5e62c283aca2e38657938bc1fec1051df728 -F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca +F src/shell.c c128bf92b3ed268d8ec6f0df275a5d737edf2968 F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc @@ -1152,7 +1152,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P f2504089df0bf4011864e67825b37f6aa3d03458 -R f919fd88b0fe2a17b41d9c03ff44720f -U mistachkin -Z fecb65c3944a970d4d7662e030506246 +P 4a4dd371a72b7d475185923bebb4cd9bd83e1bd9 +R 235c07c6649435d22d14c7aa202be2ed +U drh +Z b9d162ba79528a4d7df551b82e7b6e75 diff --git a/manifest.uuid b/manifest.uuid index e178daa36c..98d249ad3e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4a4dd371a72b7d475185923bebb4cd9bd83e1bd9 \ No newline at end of file +d1dfadea87ecf18eeb6d2f21769deaa97473ca0e \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 1c4c4ad3e3..68f2ef2afc 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1581,6 +1581,7 @@ static char zHelp[] = ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" + ".repair NEWDB Recover data into NEWDB from a corrupt database\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" @@ -1896,6 +1897,195 @@ static char *csv_read_one_field(CSVReader *p){ return p->z; } +/* +** Try to transfer data for table zTable +*/ +static void tryToRepairData( + struct callback_data *p, + sqlite3 *newDb, + const char *zTable +){ + sqlite3_stmt *pQuery = 0; + sqlite3_stmt *pInsert = 0; + char *zQuery = 0; + char *zInsert = 0; + int rc; + int i, j, n; + int nTable = (int)strlen(zTable); + int k = 0; + + zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + if( rc ){ + fprintf(stderr, "Error: (%d) %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), + zQuery); + goto end_data_xfer; + } + n = sqlite3_column_count(pQuery); + zInsert = sqlite3_malloc(200 + nTable + n*3); + if( zInsert==0 ){ + fprintf(stderr, "out of memory\n"); + goto end_data_xfer; + } + sqlite3_snprintf(200+nTable,zInsert, + "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); + i = (int)strlen(zInsert); + for(j=1; jdb, zQuery, -1, &pQuery, 0); + if( rc ){ + fprintf(stderr, "Error: (%d) %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), + zQuery); + goto end_data_xfer; + } + } /* End for(k=0...) */ + +end_data_xfer: + sqlite3_finalize(pQuery); + sqlite3_finalize(pInsert); + sqlite3_free(zQuery); + sqlite3_free(zInsert); +} + + +/* +** Try to transfer all rows of the schema that match zWhere. For +** each row, invoke xForEach() on the object defined by that row. +*/ +static void tryToRepairSchema( + struct callback_data *p, + sqlite3 *newDb, + const char *zWhere, + void (*xForEach)(struct callback_data*,sqlite3*,const char*) +){ + sqlite3_stmt *pQuery = 0; + char *zQuery = 0; + int rc; + const unsigned char *zName; + const unsigned char *zSql; + + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" + " WHERE %s", zWhere); + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + if( rc ){ + fprintf(stderr, "Error: (%d) %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), + zQuery); + goto end_schema_xfer; + } + while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ + zName = sqlite3_column_text(pQuery, 0); + zSql = sqlite3_column_text(pQuery, 1); + printf("%s... ", zName); fflush(stdout); + sqlite3_exec(newDb, (const char*)zSql, 0, 0, 0); + if( xForEach ){ + xForEach(p, newDb, (const char*)zName); + } + printf("done\n"); + } + if( rc!=SQLITE_DONE ){ + sqlite3_finalize(pQuery); + sqlite3_free(zQuery); + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" + " WHERE %s ORDER BY rowid DESC", zWhere); + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); + if( rc ){ + fprintf(stderr, "Error: (%d) %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), + zQuery); + goto end_schema_xfer; + } + while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ + zName = sqlite3_column_text(pQuery, 0); + zSql = sqlite3_column_text(pQuery, 1); + printf("%s... ", zName); fflush(stdout); + sqlite3_exec(newDb, (const char*)zSql, 0, 0, 0); + if( xForEach ){ + xForEach(p, newDb, (const char*)zName); + } + printf("done\n"); + } + } +end_schema_xfer: + sqlite3_finalize(pQuery); + sqlite3_free(zQuery); +} + +/* +** Open a new database file named "zNewDb". Try to recover as much information +** as possible out of the main database (which might be corrupt) and write it +** into zNewDb. +*/ +static void tryToRepair(struct callback_data *p, const char *zNewDb){ + int rc; + sqlite3 *newDb = 0; + if( access(zNewDb,0)==0 ){ + fprintf(stderr, "File \"%s\" already exists.\n", zNewDb); + return; + } + rc = sqlite3_open(zNewDb, &newDb); + if( rc ){ + fprintf(stderr, "Cannot create output database: %s\n", + sqlite3_errmsg(newDb)); + }else{ + sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); + tryToRepairSchema(p, newDb, "type='table'", tryToRepairData); + tryToRepairSchema(p, newDb, "type!='table'", 0); + sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); + } + sqlite3_close(newDb); +} + /* ** If an input line begins with "." then invoke this routine to ** process that line. @@ -2495,6 +2685,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){ } }else + if( c=='r' && strncmp(azArg[0], "repair", n)==0 && nArg>1 && nArg<3 ){ + tryToRepair(p, azArg[1]); + }else + if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){ const char *zSrcFile; const char *zDb;