Add the ".recovery" command to the shell tool. For recovering the maximum amount data from corrupt databases. Still needs work.

FossilOrigin-Name: 7461d2e120f2149315ddac2676d51d7445bcdb8e97543effd9c30603517ef9da
This commit is contained in:
dan 2019-04-20 20:57:28 +00:00
parent 3b412ac247
commit 68cb86ef23
6 changed files with 375 additions and 125 deletions

View File

@ -63,11 +63,11 @@
** schema TEXT HIDDEN
** );
*/
#if !defined(SQLITEINT_H)
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned long u32;
#endif
SQLITE_EXTENSION_INIT1
@ -94,8 +94,11 @@ struct DbdataCursor {
/* Only for the sqlite_dbdata table */
u8 *pRec; /* Buffer containing current record */
int nRec; /* Size of pRec[] in bytes */
int nField; /* Number of fields in pRec */
int nHdr; /* Size of header in bytes */
int iField; /* Current field number */
u8 *pHdrPtr;
u8 *pPtr;
sqlite3_int64 iIntkey; /* Integer key value */
};
@ -306,6 +309,78 @@ static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){
return 9;
}
static int dbdataValueBytes(int eType){
switch( eType ){
case 0: case 8: case 9:
case 10: case 11:
return 0;
case 1:
return 1;
case 2:
return 2;
case 3:
return 3;
case 4:
return 4;
case 5:
return 6;
case 6:
case 7:
return 8;
default:
return ((eType-12) / 2);
}
}
static void dbdataValue(sqlite3_context *pCtx, int eType, u8 *pData){
switch( eType ){
case 0:
case 10:
case 11:
sqlite3_result_null(pCtx);
break;
case 8:
sqlite3_result_int(pCtx, 0);
break;
case 9:
sqlite3_result_int(pCtx, 1);
break;
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
sqlite3_uint64 v = (signed char)pData[0];
pData++;
switch( eType ){
case 7:
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 4: v = (v<<8) + pData[0]; pData++;
case 3: v = (v<<8) + pData[0]; pData++;
case 2: v = (v<<8) + pData[0]; pData++;
}
if( eType==7 ){
double r;
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(pCtx, r);
}else{
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
}
break;
}
default: {
int n = ((eType-12) / 2);
if( eType % 2 ){
sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT);
}else{
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
}
}
}
}
/*
** Move a dbdata cursor to the next entry in the file.
*/
@ -435,20 +510,23 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
}
}
/* Figure out how many fields in the record */
pCsr->nField = 0;
iHdr = dbdataGetVarint(pCsr->pRec, &nHdr);
while( iHdr<nHdr ){
sqlite3_int64 iDummy;
iHdr += dbdataGetVarint(&pCsr->pRec[iHdr], &iDummy);
pCsr->nField++;
pCsr->nHdr = nHdr;
pCsr->pHdrPtr = &pCsr->pRec[iHdr];
pCsr->pPtr = &pCsr->pRec[pCsr->nHdr];
pCsr->iField = (bHasRowid ? -1 : 0);
}else{
pCsr->iField++;
if( pCsr->iField>0 ){
sqlite3_int64 iType;
pCsr->pHdrPtr += dbdataGetVarint(pCsr->pHdrPtr, &iType);
pCsr->pPtr += dbdataValueBytes(iType);
}
pCsr->iField = (bHasRowid ? -2 : -1);
}
pCsr->iField++;
if( pCsr->iField<pCsr->nField ) return SQLITE_OK;
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){
return SQLITE_OK;
}
/* Advance to the next cell. The next iteration of the loop will load
** the record and so on. */
@ -485,7 +563,7 @@ static int dbdataFilter(
dbdataResetCursor(pCsr);
assert( pCsr->iPgno==1 );
if( idxNum & 0x01 ){
zSchema = sqlite3_value_text(argv[0]);
zSchema = (const char*)sqlite3_value_text(argv[0]);
}
if( idxNum & 0x02 ){
pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]);
@ -498,6 +576,8 @@ static int dbdataFilter(
);
if( rc==SQLITE_OK ){
rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT);
}else{
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
if( rc==SQLITE_OK ){
rc = dbdataNext(pCursor);
@ -505,77 +585,6 @@ static int dbdataFilter(
return rc;
}
static int dbdataValueBytes(int eType){
switch( eType ){
case 0: case 8: case 9:
case 10: case 11:
return 0;
case 1:
return 1;
case 2:
return 2;
case 3:
return 3;
case 4:
return 4;
case 5:
return 6;
case 6:
case 7:
return 8;
default:
return ((eType-12) / 2);
}
}
static void dbdataValue(sqlite3_context *pCtx, int eType, u8 *pData){
switch( eType ){
case 0:
case 10:
case 11:
sqlite3_result_null(pCtx);
break;
case 8:
sqlite3_result_int(pCtx, 0);
break;
case 9:
sqlite3_result_int(pCtx, 1);
break;
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
sqlite3_uint64 v = (signed char)pData[0];
pData++;
switch( eType ){
case 7:
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 4: v = (v<<8) + pData[0]; pData++;
case 3: v = (v<<8) + pData[0]; pData++;
case 2: v = (v<<8) + pData[0]; pData++;
}
if( eType==7 ){
double r;
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(pCtx, r);
}else{
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
}
break;
}
default: {
int n = ((eType-12) / 2);
if( eType % 2 ){
sqlite3_result_text(pCtx, pData, n, SQLITE_TRANSIENT);
}else{
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
}
}
}
}
/* Return a column for the sqlite_dbdata table */
static int dbdataColumn(
sqlite3_vtab_cursor *pCursor,
@ -616,18 +625,9 @@ static int dbdataColumn(
if( pCsr->iField<0 ){
sqlite3_result_int64(ctx, pCsr->iIntkey);
}else{
int iHdr;
sqlite3_int64 iType;
sqlite3_int64 iOff;
int i;
iHdr = dbdataGetVarint(pCsr->pRec, &iOff);
for(i=0; i<pCsr->iField; i++){
iHdr += dbdataGetVarint(&pCsr->pRec[iHdr], &iType);
iOff += dbdataValueBytes(iType);
}
dbdataGetVarint(&pCsr->pRec[iHdr], &iType);
dbdataValue(ctx, iType, &pCsr->pRec[iOff]);
dbdataGetVarint(pCsr->pHdrPtr, &iType);
dbdataValue(ctx, iType, pCsr->pPtr);
}
break;
}

View File

@ -738,6 +738,7 @@ SHELL_SRC = \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/misc/zipfile.c \
$(TOP)/ext/misc/memtrace.c \
$(TOP)/ext/misc/dbdata.c \
$(TOP)/src/test_windirent.c
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl

View File

@ -1,5 +1,5 @@
C Add\sthe\ssqlite_dbptr\svirtual\stable\sto\sthe\sdbdata\sextension.\sFor\squerying\sthe\slinks\sbetween\sb-tree\spages.
D 2019-04-18T21:14:11.260
C Add\sthe\s".recovery"\scommand\sto\sthe\sshell\stool.\sFor\srecovering\sthe\smaximum\samount\sdata\sfrom\scorrupt\sdatabases.\sStill\sneeds\swork.
D 2019-04-20T20:57:28.136
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -284,7 +284,7 @@ F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c8
F ext/misc/completion.c cec672d40604075bb341a7f11ac48393efdcd90a979269b8fe7977ea62d0547f
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
F ext/misc/csv.c 7f047aeb68f5802e7ce6639292095d622a488bb43526ed04810e0649faa71ceb
F ext/misc/dbdata.c 20d85d7d7503817a5f9ac7d93c4ae7bf5fcc40570a77adc417acab0500058c99
F ext/misc/dbdata.c 8f74f25565e1f57942f75134ef94ab73058849bed87d92be619be7d3b8a7d054
F ext/misc/dbdump.c baf6e37447c9d6968417b1cd34cbedb0b0ab3f91b5329501d8a8d5be3287c336
F ext/misc/eval.c 4b4757592d00fd32e44c7a067e6a0e4839c81a4d57abc4131ee7806d1be3104e
F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
@ -441,7 +441,7 @@ F ext/userauth/userauth.c f81aa5a3ecacf406f170c62a144405858f6f6de51dbdc0920134e6
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 23d3660f7053d196aef76938bf78b10fc3ce1831a85d96bd71565758788f34d4
F main.mk 125adda36bb32c99dc3a11340bd029ef373b9523eac2b2af76087bfe82d4fdf8
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@ -520,7 +520,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
F src/select.c 9263f5c30dd44c7ac2eb29f40a7ec64322a96885b71c00de6bc30b756c2e1c49
F src/shell.c.in c1986496062f9dba4ed5b70db06b5e0f32e1954cdcfab0b30372c6c186796810
F src/shell.c.in 3646e448cc207fa3118266c2a23f177306b068e42aae6fee64323ffb13307680
F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
@ -1754,7 +1754,7 @@ F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 49039adedafbc430d2959400da2e0e8f20ef8dcf6898e447c946e7d50ef5906b
F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5
F tool/mkshellc.tcl d18a6769f766bd5a0fd5fd60e6bbde38ed6d1a41fced5fe32ac01ab9d2e0d5ec
F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
@ -1820,7 +1820,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P a3ab58832935e1399ecc7e4d8daefa3a6afa6b301792ce7176bc5d7c173510fb
R 901e9e476021499846a29c847f49c578
P 3213a15f2133afbb0a4fec3b8f6e0eeca8c0befafd6658c41074e84f589d5d32
R 9a7e323fe9ebfe9eb0759e1c70136795
U dan
Z e0c2ac18f5bd53328c7a0485e8c7ef97
Z 100e3ccaddf1d19d0bacd74d773a93b7

View File

@ -1 +1 @@
3213a15f2133afbb0a4fec3b8f6e0eeca8c0befafd6658c41074e84f589d5d32
7461d2e120f2149315ddac2676d51d7445bcdb8e97543effd9c30603517ef9da

View File

@ -948,6 +948,8 @@ INCLUDE ../ext/misc/sqlar.c
INCLUDE ../ext/expert/sqlite3expert.h
INCLUDE ../ext/expert/sqlite3expert.c
INCLUDE ../ext/misc/dbdata.c
#if defined(SQLITE_ENABLE_SESSION)
/*
** State information for a single open session
@ -1102,6 +1104,7 @@ struct ShellState {
#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
#define SHFLG_Recover 0x00000080 /* .dump is --recover */
/*
** Macros for testing and setting shellFlgs
@ -3999,6 +4002,7 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_fileio_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_completion_init(p->db, 0, 0);
sqlite3_dbdata_init(p->db, 0, 0);
#ifdef SQLITE_HAVE_ZLIB
sqlite3_zipfile_init(p->db, 0, 0);
sqlite3_sqlar_init(p->db, 0, 0);
@ -6026,6 +6030,238 @@ end_ar_command:
**********************************************************************************/
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
int rc = *pRc;
if( rc==SQLITE_OK ){
char *zErr = 0;
rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
if( rc!=SQLITE_OK ){
raw_printf(stderr, "SQL error: %s\n", zErr);
}
*pRc = rc;
}
}
static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
void *pRet = 0;
if( *pRc==SQLITE_OK ){
pRet = sqlite3_malloc64(nByte);
if( pRet==0 ){
*pRc = SQLITE_NOMEM;
}else{
memset(pRet, 0, nByte);
}
}
return pRet;
}
static char *shellMPrintf(int *pRc, const char *zFmt, ...){
char *z = 0;
if( *pRc==SQLITE_OK ){
va_list ap;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
if( z==0 ){
*pRc = SQLITE_NOMEM;
}
}
return z;
}
typedef struct RecoverTable RecoverTable;
struct RecoverTable {
char *zName; /* Name of table */
char *zQuoted; /* Quoted version of zName */
char *zCreate; /* SQL to create table in default schema */
int nCol; /* Number of columns in table */
char **azlCol; /* Array of column lists */
};
/*
** Free a RecoverTable object allocated by recoverNewTable()
*/
static void recoverFreeTable(RecoverTable *pTab){
if( pTab ){
sqlite3_free(pTab->zName);
sqlite3_free(pTab->zQuoted);
sqlite3_free(pTab->zCreate);
if( pTab->azlCol ){
int i;
for(i=0; i<pTab->nCol; i++){
sqlite3_free(pTab->azlCol[i]);
}
sqlite3_free(pTab->azlCol);
}
sqlite3_free(pTab);
}
}
static RecoverTable *recoverNewTable(
ShellState *pState,
int *pRc,
int iRoot,
int nCol
){
RecoverTable *pRet = 0;
pRet = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable));
if( pRet ){
sqlite3_stmt *pStmt = 0;
pRet->zName = shellMPrintf(pRc, "orphan_%d_%d", nCol, iRoot);
pRet->zQuoted = shellMPrintf(pRc, "%Q", pRet->zName);
pRet->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * nCol);
pRet->nCol = nCol;
shellPreparePrintf(pState->db, pRc, &pStmt,
"WITH s(i) AS ("
" SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<%d"
")"
"SELECT i-1, group_concat('c' || i, ', ') OVER (ORDER BY i) FROM s",
nCol
);
while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
int idx = sqlite3_column_int(pStmt, 0);
const char *zText = (const char*)sqlite3_column_text(pStmt, 1);
pRet->azlCol[idx] = shellMPrintf(pRc, "%s", zText);
}
shellFinalize(pRc, pStmt);
pRet->zCreate = shellMPrintf(pRc, "CREATE TABLE %Q (id, %s)",
pRet->zName, pRet->azlCol[nCol-1]
);
}
if( *pRc!=SQLITE_OK ){
recoverFreeTable(pRet);
pRet = 0;
}
return pRet;
}
/*
** This function is called to recover data from the database. A script
** to construct a new database containing all recovered data is output
** on stream pState->out.
*/
static int recoverDatabaseCmd(ShellState *pState){
const char *zSql;
int rc = SQLITE_OK;
sqlite3_stmt *pLoop = 0; /* Loop through all root pages */
shellExec(pState->db, &rc,
/* Attach an in-memory database named 'recovery'. Create an indexed
** cache of the sqlite_dbptr virtual table. */
"ATTACH '' AS recovery;"
"CREATE TABLE recovery.dbptr("
" pgno, child, PRIMARY KEY(child, pgno)"
") WITHOUT ROWID;"
"INSERT OR IGNORE INTO dbptr(pgno, child) SELECT * FROM sqlite_dbptr;"
/* Delete any pointer to page 1. This ensures that page 1 is considered
** a root page, regardless of how corrupt the db is. */
"DELETE FROM recovery.dbptr WHERE child = 1;"
/* Delete all pointers to any pages that have more than one pointer
** to them. Such pages will be treated as root pages when recovering
** data. */
"DELETE FROM recovery.dbptr WHERE child IN ("
" SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1"
");"
/* Create the "map" table that will (eventually) contain instructions
** for dealing with each page in the db that contains one or more
** records. */
"CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, maxlen INT, root INT);"
/* Populate table [map]. If there are circular loops of pages in the
** database, the following adds all pages in such a loop to the map
** as individual root pages. This could be handled better. */
"WITH pages(i, maxlen) AS ("
" SELECT page_count, max(field+1) "
" FROM pragma_page_count, sqlite_dbdata WHERE pgno=page_count"
" UNION ALL"
" SELECT * FROM (SELECT i-1, max(field+1)"
" FROM pages, sqlite_dbdata WHERE pgno=i-1 AND i>=2)"
")"
"INSERT INTO recovery.map(pgno, maxlen, root) SELECT i, maxlen, ("
" WITH p(orig, pgno, parent) AS ("
" SELECT 0, i, (SELECT pgno FROM recovery.dbptr WHERE child=i)"
" UNION ALL"
" SELECT i, p.parent, "
" (SELECT pgno FROM recovery.dbptr WHERE child=p.parent) FROM p"
" )"
" SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)"
") "
"FROM pages WHERE maxlen > 0;"
/* Extract data from page 1 and any linked pages into table
** recovery.schema. With the same schema as an sqlite_master table. */
"CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);"
"INSERT INTO recovery.schema SELECT "
" max(CASE WHEN field=0 THEN value ELSE NULL END),"
" max(CASE WHEN field=1 THEN value ELSE NULL END),"
" max(CASE WHEN field=2 THEN value ELSE NULL END),"
" max(CASE WHEN field=3 THEN value ELSE NULL END),"
" max(CASE WHEN field=4 THEN value ELSE NULL END)"
"FROM sqlite_dbdata WHERE pgno IN ("
" SELECT pgno FROM recovery.map WHERE root=1"
")"
"GROUP BY pgno, cell;"
);
#if 0
zSql = "SELECT type ||','|| name ||','|| tbl_name ||','|| rootpage ||','|| sql FROM recovery.schema;";
shellPrepare(pState->db, &rc, zSql, &pLoop);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){
raw_printf(pState->out, "%s\n", (const char*)sqlite3_column_text(pLoop, 0));
}
shellFinalize(&rc, pLoop);
return rc;
#endif
/* Loop through each root page. */
zSql = "SELECT root,max(maxlen) FROM recovery.map WHERE root>1 GROUP BY root";
shellPrepare(pState->db, &rc, zSql, &pLoop);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){
int iRoot = sqlite3_column_int(pLoop, 0);
int nCol = sqlite3_column_int(pLoop, 1);
RecoverTable *pTab;
pTab = recoverNewTable(pState, &rc, iRoot, nCol);
if( pTab ){
sqlite3_stmt *pData = 0;
raw_printf(pState->out, "%s;\n", pTab->zCreate);
shellPreparePrintf(pState->db, &rc, &pData,
"SELECT max(field), group_concat(quote(value), ', ') "
"FROM sqlite_dbdata WHERE pgno IN ("
" SELECT pgno FROM recovery.map WHERE root=%d"
")"
"GROUP BY pgno, cell;", iRoot
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pData) ){
int iMax = sqlite3_column_int(pData, 0);
const char *zVal = (const char*)sqlite3_column_text(pData, 1);
if( iMax+1==pTab->nCol ){
raw_printf(pState->out, "INSERT INTO %s VALUES( %s );\n",
pTab->zQuoted, zVal);
}else{
raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n",
pTab->zQuoted, pTab->azlCol[iMax], zVal
);
}
}
shellFinalize(&rc, pData);
}
recoverFreeTable(pTab);
}
shellFinalize(&rc, pLoop);
sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0);
return rc;
}
/*
** If an input line begins with "." then invoke this routine to
@ -6313,6 +6549,11 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = shell_dbinfo_command(p, nArg, azArg);
}else
if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
open_db(p, 0);
rc = recoverDatabaseCmd(p);
}else
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
const char *zLike = 0;
int i;
@ -6350,7 +6591,9 @@ static int do_meta_command(char *zLine, ShellState *p){
zLike = azArg[i];
}
}
open_db(p, 0);
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
@ -6365,30 +6608,30 @@ static int do_meta_command(char *zLine, ShellState *p){
p->nErr = 0;
if( zLike==0 ){
run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
);
"SELECT name, type, sql FROM sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
);
run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master "
"WHERE name=='sqlite_sequence'"
);
"SELECT name, type, sql FROM sqlite_master "
"WHERE name=='sqlite_sequence'"
);
run_table_dump_query(p,
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
);
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
);
}else{
char *zSql;
zSql = sqlite3_mprintf(
"SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE %Q AND type=='table'"
" AND sql NOT NULL", zLike);
"SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE %Q AND type=='table'"
" AND sql NOT NULL", zLike);
run_schema_dump_query(p,zSql);
sqlite3_free(zSql);
zSql = sqlite3_mprintf(
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL"
" AND type IN ('index','trigger','view')"
" AND tbl_name LIKE %Q", zLike);
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL"
" AND type IN ('index','trigger','view')"
" AND tbl_name LIKE %Q", zLike);
run_table_dump_query(p, zSql, 0);
sqlite3_free(zSql);
}
@ -6398,7 +6641,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
p->showHeader = savedShowHeader;
p->shellFlgs = savedShellFlags;
}else

View File

@ -40,16 +40,21 @@ proc omit_redundant_typedefs {line} {
}
return $line
}
set iLine 0
while {1} {
set lx [omit_redundant_typedefs [gets $in]]
if {[eof $in]} break;
incr iLine
if {[regexp {^INCLUDE } $lx]} {
set cfile [lindex $lx 1]
puts $out "/************************* Begin $cfile ******************/"
puts $out "#line 1 \"$cfile\""
set in2 [open $topdir/src/$cfile rb]
while {![eof $in2]} {
set lx [omit_redundant_typedefs [gets $in2]]
if {[regexp {^#include "sqlite} $lx]} continue
if {[regexp {^#include "sqlite} $lx]} {
set lx "/* $lx */"
}
if {[regexp {^# *include "test_windirent.h"} $lx]} {
set lx "/* $lx */"
}
@ -58,6 +63,7 @@ while {1} {
}
close $in2
puts $out "/************************* End $cfile ********************/"
puts $out "#line [expr $iLine+1] \"shell.c.in\""
continue
}
puts $out $lx