]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
More tests for the recover module.
authordan <Dan Kennedy>
Mon, 12 Sep 2022 19:23:50 +0000 (19:23 +0000)
committerdan <Dan Kennedy>
Mon, 12 Sep 2022 19:23:50 +0000 (19:23 +0000)
FossilOrigin-Name: 37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f

ext/recover/recovercorrupt.test [new file with mode: 0644]
ext/recover/recoverfault.test [new file with mode: 0644]
ext/recover/sqlite3recover.c
ext/recover/test_recover.c
manifest
manifest.uuid

diff --git a/ext/recover/recovercorrupt.test b/ext/recover/recovercorrupt.test
new file mode 100644 (file)
index 0000000..ff1d2af
--- /dev/null
@@ -0,0 +1,68 @@
+# 2022 August 28
+#
+# 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.
+#
+#***********************************************************************
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source [file join [file dirname [info script]] recover_common.tcl]
+source $testdir/tester.tcl
+
+set testprefix recovercorrupt
+
+do_execsql_test 1.0 {
+  PRAGMA page_size = 512;
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+  INSERT INTO t1 VALUES(1, 2, 3);
+  INSERT INTO t1 VALUES(2, hex(randomblob(100)), randomblob(200));
+  CREATE INDEX i1 ON t1(b, c);
+  CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
+  INSERT INTO t2 VALUES(1, 2, 3);
+  INSERT INTO t2 VALUES(2, hex(randomblob(100)), randomblob(200));
+  ANALYZE;
+  PRAGMA writable_schema = 1;
+  DELETE FROM sqlite_schema WHERE name='t2';
+}
+
+do_test 1.1 {
+  file size test.db
+} {5120}
+
+proc toggle_bit {blob bit} {
+  set byte [expr {$bit / 8}]
+  set bit [expr {$bit & 0x0F}]
+  binary scan $blob a${byte}ca* A x B
+  set x [expr {$x ^ (1 << $bit)}]
+  binary format a*ca* $A $x $B
+}
+
+
+db_save_and_close
+for {set ii 200} {$ii < 10000} {incr ii} {
+  db_restore_and_reopen
+  db func toggle_bit toggle_bit
+  set pg [expr {($ii / 512)+1}]
+  set byte [expr {$ii % 512}]
+  db eval {
+    UPDATE sqlite_dbpage SET data = toggle_bit(data, $byte) WHERE pgno=$pg
+  }
+
+  do_test 1.2.$ii {
+    set R [sqlite3_recover_init db main test.db2]
+    $R config lostandfound lost_and_found
+    $R step
+    $R finish
+  } {}
+}
+
+
+finish_test
+
diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test
new file mode 100644 (file)
index 0000000..b2411fb
--- /dev/null
@@ -0,0 +1,72 @@
+# 2022 August 28
+#
+# 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.
+#
+#***********************************************************************
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source [file join [file dirname [info script]] recover_common.tcl]
+source $testdir/tester.tcl
+
+set testprefix recoverfault
+
+
+#--------------------------------------------------------------------------
+proc compare_result {db1 db2 sql} {
+  set r1 [$db1 eval $sql]
+  set r2 [$db2 eval $sql]
+  if {$r1 != $r2} {
+    puts "r1: $r1"
+    puts "r2: $r2"
+    error "mismatch for $sql"
+  }
+  return ""
+}
+
+proc compare_dbs {db1 db2} {
+  compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1"
+  foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] {
+    compare_result $db1 $db2 "SELECT * FROM $tbl"
+  }
+}
+#--------------------------------------------------------------------------
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+  INSERT INTO t1 VALUES(1, 2, 3);
+  INSERT INTO t1 VALUES(2, hex(randomblob(1000)), randomblob(2000));
+  CREATE INDEX i1 ON t1(b, c);
+  CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
+  INSERT INTO t2 VALUES(1, 2, 3);
+  INSERT INTO t2 VALUES(2, hex(randomblob(1000)), randomblob(2000));
+  ANALYZE;
+  PRAGMA writable_schema = 1;
+  DELETE FROM sqlite_schema WHERE name='t2';
+}
+
+do_faultsim_test 1 -faults oom* -prep {
+  faultsim_restore_and_reopen
+} -body {
+  set R [sqlite3_recover_init db main test.db2]
+  $R config lostandfound lost_and_found
+  $R step
+  $R finish
+} -test {
+  faultsim_test_result {0 {}} {1 {}}
+  if {$testrc==0} {
+    sqlite3 db2 test.db2
+    compare_dbs db db2
+    db2 close
+  }
+}
+
+finish_test
+
index 9c5e10433b4986b1f6e3ba186e9a6aa3b69e7489..849c3bc80177ab85d920d33834bec0410a9cb2d0 100644 (file)
@@ -226,12 +226,13 @@ static int recoverError(
   int errCode, 
   const char *zFmt, ...
 ){
+  char *z = 0;
   va_list ap;
-  char *z;
   va_start(ap, zFmt);
-  z = sqlite3_vmprintf(zFmt, ap);
-  va_end(ap);
-
+  if( zFmt ){
+    z = sqlite3_vmprintf(zFmt, ap);
+    va_end(ap);
+  }
   sqlite3_free(p->zErrMsg);
   p->zErrMsg = z;
   p->errCode = errCode;
@@ -448,7 +449,8 @@ static i64 recoverPageCount(sqlite3_recover *p){
   if( p->errCode==SQLITE_OK ){
     sqlite3_stmt *pStmt = 0;
     pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb);
-    if( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
+    if( pStmt ){
+      sqlite3_step(pStmt);
       nPg = sqlite3_column_int64(pStmt, 0);
     }
     recoverFinalize(p, pStmt);
@@ -866,11 +868,11 @@ static void recoverAddTable(
 
       pNew->pNext = p->pTblList;
       p->pTblList = pNew;
+      pNew->bIntkey = 1;
     }
 
     recoverFinalize(p, pStmt);
 
-    pNew->bIntkey = 1;
     pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName);
     while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
       int iField = sqlite3_column_int(pStmt, 0);
@@ -884,10 +886,12 @@ static void recoverAddTable(
     }
     recoverFinalize(p, pStmt);
 
-    if( iPk>=0 ){
-      pNew->aCol[iPk].bIPK = 1;
-    }else if( pNew->bIntkey ){
-      pNew->iRowidBind = iBind++;
+    if( p->errCode==SQLITE_OK ){
+      if( iPk>=0 ){
+        pNew->aCol[iPk].bIPK = 1;
+      }else if( pNew->bIntkey ){
+        pNew->iRowidBind = iBind++;
+      }
     }
   }
 }
@@ -1068,6 +1072,7 @@ static sqlite3_stmt *recoverInsertStmt(
   RecoverTable *pTab,
   int nField
 ){
+  sqlite3_stmt *pRet = 0;
   const char *zSep = "";
   const char *zSqlSep = "";
   char *zSql = 0;
@@ -1075,7 +1080,8 @@ static sqlite3_stmt *recoverInsertStmt(
   char *zBind = 0;
   int ii;
   int bSql = p->xSql ? 1 : 0;
-  sqlite3_stmt *pRet = 0;
+
+  if( nField<=0 ) return 0;
 
   assert( nField<=pTab->nCol );
 
@@ -1525,7 +1531,7 @@ static int recoverWriteData(sqlite3_recover *p){
 
           int bNewCell = (iPrevPage!=iPage || iPrevCell!=iCell);
           assert( bNewCell==0 || (iField==-1 || iField==0) );
-          assert( bNewCell || iField==nVal );
+          assert( bNewCell || iField==nVal || nVal==pTab->nCol );
 
           if( bNewCell ){
             if( nVal>=0 ){
@@ -1538,29 +1544,31 @@ static int recoverWriteData(sqlite3_recover *p){
                 pInsert = recoverInsertStmt(p, pTab, nVal);
                 nInsert = nVal;
               }
-
-              for(ii=0; ii<pTab->nCol; ii++){
-                RecoverColumn *pCol = &pTab->aCol[ii];
-
-                if( pCol->iBind>0 ){
-                  if( pCol->bIPK ){
-                    sqlite3_bind_int64(pInsert, pCol->iBind, iRowid);
-                  }else if( pCol->iField<nVal ){
-                    sqlite3_bind_value(pInsert,pCol->iBind,apVal[pCol->iField]);
+              if( nVal>0 ){
+                for(ii=0; ii<pTab->nCol; ii++){
+                  RecoverColumn *pCol = &pTab->aCol[ii];
+
+                  if( pCol->iBind>0 ){
+                    int iBind = pCol->iBind;
+                    if( pCol->bIPK ){
+                      sqlite3_bind_int64(pInsert, iBind, iRowid);
+                    }else if( pCol->iField<nVal ){
+                      sqlite3_bind_value(pInsert, iBind, apVal[pCol->iField]);
+                    }
                   }
                 }
-              }
-              if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){
-                sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
-              }
+                if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){
+                  sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
+                }
 
-              if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){
-                const char *zSql = (const char*)sqlite3_column_text(pInsert, 0);
-                recoverSqlCallback(p, zSql);
+                if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){
+                  const char *z = (const char*)sqlite3_column_text(pInsert, 0);
+                  recoverSqlCallback(p, z);
+                }
+                recoverReset(p, pInsert);
+                assert( p->errCode || pInsert );
+                if( pInsert ) sqlite3_clear_bindings(pInsert);
               }
-              recoverReset(p, pInsert);
-              assert( p->errCode || pInsert );
-              if( pInsert ) sqlite3_clear_bindings(pInsert);
             }
 
             for(ii=0; ii<nVal; ii++){
@@ -1577,9 +1585,12 @@ static int recoverWriteData(sqlite3_recover *p){
               assert( nVal==-1 );
               nVal = 0;
               bHaveRowid = 1;
-            }else if( iField<nMax ){
+            }else if( iField<pTab->nCol ){
               assert( apVal[iField]==0 );
               apVal[iField] = sqlite3_value_dup( pVal );
+              if( apVal[iField]==0 ){
+                recoverError(p, SQLITE_NOMEM, 0);
+              }
               nVal = iField+1;
             }
             iPrevCell = iCell;
@@ -1686,7 +1697,6 @@ sqlite3_recover *recoverInit(
   int nByte = 0;
 
   if( zDb==0 ){ zDb = "main"; }
-  if( zUri==0 ){ zUri = ""; }
 
   nDb = recoverStrlen(zDb);
   nUri = recoverStrlen(zUri);
@@ -1699,7 +1709,7 @@ sqlite3_recover *recoverInit(
     pRet->zDb = (char*)&pRet[1];
     pRet->zUri = &pRet->zDb[nDb+1];
     memcpy(pRet->zDb, zDb, nDb);
-    memcpy(pRet->zUri, zUri, nUri);
+    if( nUri>0 ) memcpy(pRet->zUri, zUri, nUri);
     pRet->xSql = xSql;
     pRet->pSqlCtx = pSqlCtx;
     pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT;
@@ -1730,14 +1740,14 @@ sqlite3_recover *sqlite3_recover_init_sql(
   int (*xSql)(void*, const char*),
   void *pSqlCtx
 ){
-  return recoverInit(db, zDb, "", xSql, pSqlCtx);
+  return recoverInit(db, zDb, 0, xSql, pSqlCtx);
 }
 
 /*
 ** Return the handle error message, if any.
 */
 const char *sqlite3_recover_errmsg(sqlite3_recover *p){
-  return p ? p->zErrMsg : "not an error";
+  return (p && p->errCode!=SQLITE_NOMEM) ? p->zErrMsg : "out of memory";
 }
 
 /*
@@ -1753,6 +1763,7 @@ int sqlite3_recover_errcode(sqlite3_recover *p){
 int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){
   int rc = SQLITE_OK;
 
+  if( p==0 ) return SQLITE_NOMEM;
   switch( op ){
     case 789:
       sqlite3_free(p->zStateDb);
@@ -1791,8 +1802,12 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){
 */
 int sqlite3_recover_run(sqlite3_recover *p){
   if( p ){
+    recoverExec(p, p->dbIn, "PRAGMA writable_schema=1");
     if( p->bRun ) return SQLITE_MISUSE;     /* Has already run */
     if( p->errCode==SQLITE_OK ) recoverRun(p);
+    if( sqlite3_exec(p->dbIn, "PRAGMA writable_schema=0", 0, 0, 0) ){
+      recoverDbError(p, p->dbIn);
+    }
   }
   return p ? p->errCode : SQLITE_NOMEM;
 }
@@ -1807,11 +1822,16 @@ int sqlite3_recover_run(sqlite3_recover *p){
 ** not been called on this handle.
 */
 int sqlite3_recover_finish(sqlite3_recover *p){
-  int rc = p->errCode;
-  sqlite3_free(p->zErrMsg);
-  sqlite3_free(p->zStateDb);
-  sqlite3_free(p->zLostAndFound);
-  sqlite3_free(p);
+  int rc;
+  if( p==0 ){
+    rc = SQLITE_NOMEM;
+  }else{
+    rc = p->errCode;
+    sqlite3_free(p->zErrMsg);
+    sqlite3_free(p->zStateDb);
+    sqlite3_free(p->zLostAndFound);
+    sqlite3_free(p);
+  }
   return rc;
 }
 
index a31ddbb94ab9556cb5d277b9d937497a5d4cea45..a7c5afd3aae4894f1c16e822350a324c80ed2680 100644 (file)
@@ -165,9 +165,7 @@ static int testRecoverCmd(
       int res2;
       if( res!=SQLITE_OK ){
         const char *zErr = sqlite3_recover_errmsg(pTest->p);
-        char *zRes = sqlite3_mprintf("(%d) - %s", res, zErr);
-        Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes, -1));
-        sqlite3_free(zRes);
+        Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
       }
       res2 = sqlite3_recover_finish(pTest->p);
       assert( res2==res );
index d505905e12b86e44e73abffa474baad652995a0a..c61b4a13ae335dcbe248318755b5c0f9dd6e5996 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Rework\srecover\sextension\scode\sfor\sreadability.
-D 2022-09-10T20:01:49.308
+C More\stests\sfor\sthe\srecover\smodule.
+D 2022-09-12T19:23:50.588
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -390,11 +390,13 @@ F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f5974282
 F ext/recover/recover1.test 942016356f9098ca36933536b194b5878827a3a749e0bf41a83d83530c0d0ea8
 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c
 F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84
+F ext/recover/recovercorrupt.test 115cdb67ac29b4e8ec786cee9190ced674f62388f126b20deea22fa5fd11b814
+F ext/recover/recoverfault.test 30e3d1b423b33b4c57f4a97f9754b2d493a411d858b2672ab369d65199bc2420
 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074
 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417
-F ext/recover/sqlite3recover.c 4ed53fd33639ede83505f4397b8e1e46b6c0c5a5188c42f28f8372487141170a
+F ext/recover/sqlite3recover.c d40d7c68a118e01d2b06c96325bbe3b85701f3add2ab5aeec289eeafd5e36d6c
 F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640
-F ext/recover/test_recover.c ed8d0cc8703ab29cf562f793623b045de109b7937f254108ff4132f35abb37fb
+F ext/recover/test_recover.c 8f5ef0c9b7523c41a393f65e44d727c23cda8f44d5180fff5b698ee068ba538d
 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996
 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890
@@ -2007,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99
-R cd56b05532a91c08b9e673fffba829f5
+P 1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a
+R 91d8f87cd1493a401d98e4544c1e3f6e
 U dan
-Z 624cfda4b7c658530a41a87671068e55
+Z d3a21f62871d8e066c6c2bd48e5f7060
 # Remove this line to create a well-formed Fossil manifest.
index 98f37f05634e7a58d9384123df3647aa47071354..340320e0cf6fcd8a52f4aa4c9527f33ddc425a78 100644 (file)
@@ -1 +1 @@
-1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a
\ No newline at end of file
+37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f
\ No newline at end of file