]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add implementation of sqlite3_intck_suspend().
authordan <Dan Kennedy>
Mon, 19 Feb 2024 18:03:53 +0000 (18:03 +0000)
committerdan <Dan Kennedy>
Mon, 19 Feb 2024 18:03:53 +0000 (18:03 +0000)
FossilOrigin-Name: c36ada868da74e030ff5002de1f3b31b639b0c43714b91c2e5ca0eda16bb6bc2

ext/intck/intck1.test
ext/intck/intck2.test
ext/intck/intck_common.tcl
ext/intck/sqlite3intck.c
ext/intck/test_intck.c
manifest
manifest.uuid

index c46a7d6b2db8050ea01ca53b91f372580bb9c35d..e5a25dda8e83ca365ff18c056649f33e64316939 100644 (file)
@@ -167,8 +167,8 @@ do_test 3.2 {
   sqlite3 db test.db
 } {}
 
-puts "[intck_sql db x1a]"
-execsql_pp "EXPLAIN QUERY PLAN [intck_sql db x1a]"
+#puts "[intck_sql db x1a]"
+#execsql_pp "EXPLAIN QUERY PLAN [intck_sql db x1a]"
 do_intck_test 3.3 { 
   {entry (4,'six',5) missing from index x1a}
 }
@@ -185,6 +185,11 @@ do_execsql_test 4.0 {
   INSERT INTO www VALUES(1, 1, 1), (2, 2, 2);
 }
 
+#puts [intck_sql db w1]
+#execsql_pp [intck_sql db www]
+#execsql_pp [intck_sql db w1]
+#puts [intck_sql db w1]
+
 do_intck_test 4.1 { }
 
 finish_test
index bc9aebeea896a0e2f6979059154d801967f9d564..90f263f88023e3c02f87e13bc834a464a9c59410 100644 (file)
@@ -43,8 +43,8 @@ imposter_edit i1 {
 }
 
 do_intck_test 1.1 {
-  {entry ('two',2) missing from index i1}
   {surplus entry ('four',4) in index i1}
+  {entry ('two',2) missing from index i1}
 }
 
 #-------------------------------------------------------------------------
@@ -61,9 +61,7 @@ do_execsql_test 2.0 {
 }
 
 do_intck_test 2.1 {}
-puts [intck_sql db x1]
-
-
+#puts [intck_sql db x1]
 
 finish_test
 
index 0763d132663782c4cd2bc3ea41cf28a92214533a..757ca82b3f7f4935da6c35097292810716434b22 100644 (file)
@@ -15,7 +15,7 @@ if {![info exists testdir]} {
 }
 source $testdir/tester.tcl
 
-proc do_intck {db} {
+proc do_intck {db {bSuspend 0}} {
   set ic [sqlite3_intck $db main ""]
 
   set ret [list]
@@ -24,6 +24,7 @@ proc do_intck {db} {
     if {$msg!=""} {
       lappend ret $msg
     }
+    if {$bSuspend} { $ic suspend }
   }
 
   set err [$ic error]
@@ -43,7 +44,8 @@ proc intck_sql {db tbl} {
 }
 
 proc do_intck_test {tn expect} {
-  uplevel [list do_test $tn [list do_intck db] [list {*}$expect]]
+  uplevel [list do_test $tn.a [list do_intck db] [list {*}$expect]]
+  uplevel [list do_test $tn.b [list do_intck db 1] [list {*}$expect]]
 }
 
 
index 7610d44a9baab4b8b7d3037603851d510c5145c1..b08b499a4370d27a8c6dd5505d99b9464d0de2e6 100644 (file)
@@ -20,10 +20,10 @@ struct sqlite3_intck {
   sqlite3 *db;
   const char *zDb;                /* Copy of zDb parameter to _open() */
 
-  sqlite3_stmt *pListTables;
+  char *zObj;                     /* Current object. Or NULL. */
+  char *zKey;                     /* Key saved by _suspect() call. */
 
   sqlite3_stmt *pCheck;
-  int nCheck;
 
   int rc;                         /* SQLite error code */
   char *zErr;                     /* Error message */
@@ -58,11 +58,15 @@ static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zFmt, ...){
       p->rc = sqlite3_prepare_v2(p->db, zSql, -1, &pRet, 0);
       fflush(stdout);
       if( p->rc!=SQLITE_OK ){
+#if 1
       printf("ERROR: %s\n", zSql);
       printf("MSG: %s\n", sqlite3_errmsg(p->db));
+#endif
       if( sqlite3_error_offset(p->db)>=0 ){
+#if 1
         int iOff = sqlite3_error_offset(p->db);
         printf("AT: %.40s\n", &zSql[iOff]);
+#endif
       }
       fflush(stdout);
         intckSaveErrmsg(p);
@@ -82,27 +86,6 @@ static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
   }
 }
 
-/*
-** Return an SQL statement that will itself return a single row for each
-** table in the target schema. The row contains two columns:
-**
-**   0: table_name - name of table
-**   1: without_rowid - true for WITHOUT ROWID tables, false otherwise.
-**
-*/
-static sqlite3_stmt *intckListTables(sqlite3_intck *p){
-  return intckPrepare(p, 
-    "WITH tables(table_name) AS (" 
-    "  SELECT name"
-    "  FROM %Q.sqlite_schema WHERE type='table' OR type='index'"
-    "  UNION ALL "
-    "  SELECT 'sqlite_schema'"
-    ")"
-    "SELECT * FROM tables"
-    , p->zDb
-  );
-}
-
 static char *intckStrdup(sqlite3_intck *p, const char *zIn){
   char *zOut = 0;
   if( p->rc==SQLITE_OK ){
@@ -117,6 +100,42 @@ static char *intckStrdup(sqlite3_intck *p, const char *zIn){
   return zOut;
 }
 
+static void intckFindObject(sqlite3_intck *p){
+  sqlite3_stmt *pStmt = 0;
+  char *zPrev = p->zObj;
+  p->zObj = 0;
+
+  assert( p->rc==SQLITE_OK );
+  pStmt = intckPrepare(p, 
+    "WITH tables(table_name) AS (" 
+    "  SELECT name"
+    "  FROM %Q.sqlite_schema WHERE type='table' OR type='index'"
+    "  UNION ALL "
+    "  SELECT 'sqlite_schema'"
+    ")"
+    "SELECT table_name FROM tables "
+    "WHERE ?1 IS NULL OR table_name%s?1 "
+    "ORDER BY 1"
+    , p->zDb, (p->zKey ? ">=" : ">")
+  );
+
+  if( p->rc==SQLITE_OK ){
+    sqlite3_bind_text(pStmt, 1, zPrev, -1, SQLITE_TRANSIENT);
+    if( sqlite3_step(pStmt)==SQLITE_ROW ){
+      p->zObj = intckStrdup(p, (const char*)sqlite3_column_text(pStmt, 0));
+    }
+  }
+  intckFinalize(p, pStmt);
+
+  /* If this is a new object, ensure the previous key value is cleared. */
+  if( sqlite3_stricmp(p->zObj, zPrev) ){
+    sqlite3_free(p->zKey);
+    p->zKey = 0;
+  }
+
+  sqlite3_free(zPrev);
+}
+
 /*
 ** Return the size in bytes of the first token in nul-terminated buffer z.
 ** For the purposes of this call, a token is either:
@@ -314,7 +333,11 @@ static void intckExec(sqlite3_intck *p, const char *zSql){
   intckFinalize(p, pStmt);
 }
 
-static char *intckCheckObjectSql(sqlite3_intck *p, const char *zObj){
+static char *intckCheckObjectSql(
+  sqlite3_intck *p, 
+  const char *zObj,
+  const char *zPrev
+){
   char *zRet = 0;
   sqlite3_stmt *pStmt = 0;
   int bAutoIndex = 0;
@@ -427,45 +450,61 @@ static char *intckCheckObjectSql(sqlite3_intck *p, const char *zObj){
       /* Table idxname contains a single row. The first column, "db", contains
       ** the name of the db containing the table (e.g. "main") and the second,
       ** "tab", the name of the table itself.  */
-      "WITH tabname(db, tab, idx) AS ("
-      "  SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), %Q "
+      "WITH tabname(db, tab, idx, prev) AS ("
+      "  SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), "
+      "         %Q, %Q "
       ")"
       "%s" /* zCommon */
       ""
       ", case_statement(c) AS ("
       "  SELECT "
-      "    'CASE WHEN (' || group_concat(col_alias, ', ') || ') IS (\n  ' "
+      "    'CASE WHEN (' || group_concat(col_alias, ', ') || ') IS (\n    ' "
       "    || 'SELECT ' || group_concat(col_expr, ', ') || ' FROM '"
-      "    || format('%%Q.%%Q NOT INDEXED WHERE %%s\n)', t.db, t.tab, p.eq_pk)"
-      "    || '\nTHEN NULL\n'"
+      "    || format('%%Q.%%Q NOT INDEXED WHERE %%s\n', t.db, t.tab, p.eq_pk)"
+      "    || '  )\n  THEN NULL\n  '"
       "    || 'ELSE format(''surplus entry ('"
       "    ||   group_concat('%%s', ',') || ',' || p.ps_pk"
       "    || ') in index ' || t.idx || ''', ' "
       "    ||   group_concat('quote('||i.col_alias||')', ', ') || ', ' || p.pk_pk"
       "    || ')'"
-      "    || '\nEND'"
+      "    || '\nEND AS error_message'"
       "  FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx"
+      ")"
       ""
+      ", thiskey(k) AS ("
+      "    SELECT format('format(''(%%s,%%s)'', %%s, %%s) AS thiskey', "
+      "        group_concat('%%s', ','), p.ps_pk, "
+      "        group_concat('quote('||i.col_alias||')',', '), p.pk_pk"
+      "    ) FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx"
+      ")"
+      ""
+      ", whereclause(w_c) AS ("
+      "    SELECT CASE WHEN prev!='' THEN "
+      "    '\nWHERE (' || group_concat(i.col_alias, ',') || ',' "
+      "                || o_pk || ') > ' || prev"
+      "    ELSE ''"
+      "    END"
+      "    FROM tabpk, tabname, idx_cols i WHERE i.idx_name=tabpk.idx"
       ")"
       ""
       ", main_select(m) AS ("
       "  SELECT format("
-      "      'WITH %%s\nSELECT %%s\nFROM intck_wrapper AS o',"
-      "      ww.s, c"
+      "      'WITH %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o%%s',"
+      "      ww.s, c, t.k, whereclause.w_c"
       "  )"
-      "  FROM case_statement, wrapper_with ww"
+      "  FROM case_statement, wrapper_with ww, thiskey t, whereclause"
       ")"
 
       "SELECT m FROM main_select"
       , p->zDb, p->zDb, zObj, zObj
-      , zCommon
+      , zPrev, zCommon
       );
   }else{
     pStmt = intckPrepare(p,
       /* Table tabname contains a single row. The first column, "db", contains
       ** the name of the db containing the table (e.g. "main") and the second,
       ** "tab", the name of the table itself.  */
-      "WITH tabname(db, tab, idx) AS (SELECT %Q, %Q, NULL)"
+      "WITH tabname(db, tab, idx, prev) AS (SELECT %Q, %Q, NULL, %Q)"
       ""
       "%s" /* zCommon */
 
@@ -505,18 +544,41 @@ static char *intckCheckObjectSql(sqlite3_intck *p, const char *zObj){
       "    '\nEND AS error_message'"
       "    FROM numbered"
       ")"
+      ""
 
+      /* This table contains a single row consisting of a single value -
+      ** the text of an SQL expression that may be used by the main SQL
+      ** statement to output an SQL literal that can be used to resume
+      ** the scan if it is suspended. e.g. for a rowid table, an expression
+      ** like:
+      **
+      **     format('(%d,%d)', _rowid_, n.ii)
+      */
+      ", thiskey(k) AS ("
+      "    SELECT 'format(''(' || ps_pk || ',%%d)'', ' || pk_pk || ', n.ii)'"
+      "    FROM tabpk"
+      ")"
+      ""
+      ", whereclause(w_c) AS ("
+      "    SELECT CASE WHEN prev!='' THEN "
+      "    '\nWHERE (' || o_pk ||', n.ii) > ' || prev"
+      "    ELSE ''"
+      "    END"
+      "    FROM tabpk, tabname"
+      ")"
+      ""
       ", main_select(m) AS ("
       "  SELECT format("
-      "      '%%s, %%s\nSELECT %%s\nFROM intck_wrapper AS o"
-               ", intck_counter AS n ORDER BY %%s', "
-      "      w, ww.s, c, t.o_pk"
+      "      '%%s, %%s\nSELECT %%s,\n%%s AS thiskey\nFROM intck_wrapper AS o"
+               ", intck_counter AS n%%s\nORDER BY %%s', "
+      "      w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk"
       "  )"
-      "  FROM case_statement, tabpk t, counter_with, wrapper_with ww"
+      "  FROM case_statement, tabpk t, counter_with, "
+      "       wrapper_with ww, thiskey, whereclause"
       ")"
 
       "SELECT m FROM main_select",
-      p->zDb, zObj, zCommon
+      p->zDb, zObj, zPrev, zCommon
     );
   }
 
@@ -542,10 +604,11 @@ static char *intckCheckObjectSql(sqlite3_intck *p, const char *zObj){
 }
 
 static void intckCheckObject(sqlite3_intck *p){
-  const char *zTab = (const char*)sqlite3_column_text(p->pListTables, 0);
-  char *zSql = intckCheckObjectSql(p, zTab);
+  char *zSql = intckCheckObjectSql(p, p->zObj, p->zKey);
   p->pCheck = intckPrepare(p, "%s", zSql);
   sqlite3_free(zSql);
+  sqlite3_free(p->zKey);
+  p->zKey = 0;
 }
 
 int sqlite3_intck_open(
@@ -582,6 +645,8 @@ void sqlite3_intck_close(sqlite3_intck *p){
         p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0
     );
   }
+  sqlite3_free(p->zObj);
+  sqlite3_free(p->zKey);
   sqlite3_free(p->zTestSql);
   sqlite3_free(p->zErr);
   sqlite3_free(p);
@@ -589,26 +654,19 @@ void sqlite3_intck_close(sqlite3_intck *p){
 
 int sqlite3_intck_step(sqlite3_intck *p){
   if( p->rc==SQLITE_OK ){
-    if( p->pListTables==0 ){
-      p->pListTables = intckListTables(p);
-    }
-    assert( p->pListTables || p->rc!=SQLITE_OK );
-
-    if( p->rc==SQLITE_OK && p->pCheck==0 ){
-      if( sqlite3_step(p->pListTables)==SQLITE_ROW ){
-        intckCheckObject(p);
-      }else{
-        int rc = sqlite3_finalize(p->pListTables);
-        if( rc==SQLITE_OK ){
-          p->rc = SQLITE_DONE;
+    if( p->pCheck==0 ){
+      intckFindObject(p);
+      if( p->rc==SQLITE_OK ){
+        if( p->zObj ){
+          intckCheckObject(p);
         }else{
-          intckSaveErrmsg(p);
+          p->rc = SQLITE_DONE;
         }
-        p->pListTables = 0;
       }
     }
 
     if( p->rc==SQLITE_OK ){
+      assert( p->pCheck );
       if( sqlite3_step(p->pCheck)==SQLITE_ROW ){
         /* Fine, whatever... */
       }else{
@@ -635,13 +693,28 @@ int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){
   return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc);
 }
 
-int sqlite3_intck_suspend(sqlite3_intck *pCk){
-  return SQLITE_OK;
+int sqlite3_intck_suspend(sqlite3_intck *p){
+  if( p->pCheck && p->rc==SQLITE_OK ){
+    assert( p->zKey==0 );
+    p->zKey = intckStrdup(p, (const char*)sqlite3_column_text(p->pCheck, 1));
+    intckFinalize(p, p->pCheck);
+    p->pCheck = 0;
+  }
+  return p->rc;
 }
 
 const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){
   sqlite3_free(p->zTestSql);
-  p->zTestSql = intckCheckObjectSql(p, zObj);
+  if( zObj ){
+    p->zTestSql = intckCheckObjectSql(p, zObj, 0);
+  }else{
+    if( p->zObj ){
+      p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey);
+    }else{
+      sqlite3_free(p->zTestSql);
+      p->zTestSql = 0;
+    }
+  }
   return p->zTestSql;
 }
 
index 33129ec275424992940e10988c6b154e3146c681..d14fc92a6c97a8c1813333208e7ff0868e2265ea 100644 (file)
@@ -113,7 +113,7 @@ static int testIntckCmd(
 
     case 5: assert( 0==strcmp("test_sql", aCmd[iIdx].zName) ); {
       const char *zObj = Tcl_GetString(objv[2]);
-      const char *zSql = sqlite3_intck_test_sql(p->intck, zObj);
+      const char *zSql = sqlite3_intck_test_sql(p->intck, zObj[0] ? zObj : 0);
       Tcl_SetObjResult(interp, Tcl_NewStringObj(zSql, -1));
       break;
     }
index a378ab14eb7144eb3d775aad15bb81559e979c1c..bb8e46b123ef1964495323cb2639128aecef4244 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sstart\sof\sextension\sfor\sincremental\sintegrity-checks\sto\sext/intck/.
-D 2024-02-17T20:55:01.343
+C Add\simplementation\sof\ssqlite3_intck_suspend().
+D 2024-02-19T18:03:53.963
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -248,12 +248,12 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
-F ext/intck/intck1.test 0fcf3696b59aff6c344553647d612921dd529600796ff7172c02679955cecdcf
-F ext/intck/intck2.test dd06719eca145b317ae380c81f04cd8a096a7cfdb71074cc6b6e7f195058b0d0
-F ext/intck/intck_common.tcl 1f2599d50033d21d5df89f5ed54cc29af472d86e3927e116db50c5ba94d903b9
-F ext/intck/sqlite3intck.c 14300998e91cd8788f483d97e53be9406f2c0be8af1867f399b80fef5e3721fb
+F ext/intck/intck1.test c831bc6ff67da3c5b6c9568640f87ad0442d4fc98ef97bc837133b94bcc645b3
+F ext/intck/intck2.test b65d7f627342f767e1d2c447d25619666cec36f516786dd56568bd741e5d7e67
+F ext/intck/intck_common.tcl 2895854e7aaf5e199a15f6f82538a00999fd8fc55553bc1f04619af7aa86c0d0
+F ext/intck/sqlite3intck.c 703ff16bc936192cff20d06b015d66279f4594e88371c00b18d17fec4f01ff5c
 F ext/intck/sqlite3intck.h 342ee2e2c7636b4daf29fa195d0a3a658272b76b283d586fba50f6bc80fc143d
-F ext/intck/test_intck.c 3f9a950978842340df7492f0a4190022979f23ff904e90873a5e262adf30b78c
+F ext/intck/test_intck.c eb596269c4a690a9b8ee689b1e52ff6e3306013ec706e319d5b97af05a08f0b9
 F ext/jni/GNUmakefile 59eb05f2a363bdfac8d15d66bed624bfe1ff289229184f3861b95f98a19cf4b2
 F ext/jni/README.md d899789a9082a07b99bf30b1bbb6204ae57c060efcaa634536fa669323918f42
 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
@@ -2168,11 +2168,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 7fca1bc482fc2456d75392eb42f768fda72631c9070de46b8123b1126e78306f
-R f8dccfe308ed2507020014beca90ae47
-T *branch * incr-integrity-check
-T *sym-incr-integrity-check *
-T -sym-trunk *
+P 444e3c9210026da7eae1ed98850722e002433aa2cc77dbc6b6f80327a6b7a390
+R 6b69910b5d388b58f98078e173b37781
 U dan
-Z 8b8b6eda2ec7249d41ba58db982258fb
+Z 3045521be4122627d743cbd6c4ab9b9c
 # Remove this line to create a well-formed Fossil manifest.
index 7ea8d90d425f491ae9fdccb2e9ee57080d207457..fa3faa50cddd2407c989f0caad2257961179e690 100644 (file)
@@ -1 +1 @@
-444e3c9210026da7eae1ed98850722e002433aa2cc77dbc6b6f80327a6b7a390
\ No newline at end of file
+c36ada868da74e030ff5002de1f3b31b639b0c43714b91c2e5ca0eda16bb6bc2
\ No newline at end of file