From: dan Date: Mon, 19 Feb 2024 18:03:53 +0000 (+0000) Subject: Add implementation of sqlite3_intck_suspend(). X-Git-Tag: version-3.46.0~193^2~16 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cfc62ceefdfb19ed162b725cdca30fdea6a055e0;p=thirdparty%2Fsqlite.git Add implementation of sqlite3_intck_suspend(). FossilOrigin-Name: c36ada868da74e030ff5002de1f3b31b639b0c43714b91c2e5ca0eda16bb6bc2 --- diff --git a/ext/intck/intck1.test b/ext/intck/intck1.test index c46a7d6b2d..e5a25dda8e 100644 --- a/ext/intck/intck1.test +++ b/ext/intck/intck1.test @@ -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 diff --git a/ext/intck/intck2.test b/ext/intck/intck2.test index bc9aebeea8..90f263f880 100644 --- a/ext/intck/intck2.test +++ b/ext/intck/intck2.test @@ -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 diff --git a/ext/intck/intck_common.tcl b/ext/intck/intck_common.tcl index 0763d13266..757ca82b3f 100644 --- a/ext/intck/intck_common.tcl +++ b/ext/intck/intck_common.tcl @@ -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]] } diff --git a/ext/intck/sqlite3intck.c b/ext/intck/sqlite3intck.c index 7610d44a9b..b08b499a43 100644 --- a/ext/intck/sqlite3intck.c +++ b/ext/intck/sqlite3intck.c @@ -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; } diff --git a/ext/intck/test_intck.c b/ext/intck/test_intck.c index 33129ec275..d14fc92a6c 100644 --- a/ext/intck/test_intck.c +++ b/ext/intck/test_intck.c @@ -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; } diff --git a/manifest b/manifest index a378ab14eb..bb8e46b123 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 7ea8d90d42..fa3faa50cd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -444e3c9210026da7eae1ed98850722e002433aa2cc77dbc6b6f80327a6b7a390 \ No newline at end of file +c36ada868da74e030ff5002de1f3b31b639b0c43714b91c2e5ca0eda16bb6bc2 \ No newline at end of file