From 41549103acdbb8f003898119d9b916dfb0fb085e Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 27 Sep 2025 13:34:02 +0000 Subject: [PATCH] ext/wasm/c-pp.c: add #savepoint support. Consolidate how the diverse sqlite3_stmt handles are managed. FossilOrigin-Name: b44650f907e9cb4ec908bb7525488e309946fac9d84cdac4cdde730527a440a9 --- ext/wasm/c-pp.c | 276 ++++++++++++++++++++++++++++-------------------- manifest | 14 +-- manifest.uuid | 2 +- 3 files changed, 169 insertions(+), 123 deletions(-) diff --git a/ext/wasm/c-pp.c b/ext/wasm/c-pp.c index d87d47ee47..dd6ebe211f 100644 --- a/ext/wasm/c-pp.c +++ b/ext/wasm/c-pp.c @@ -36,7 +36,12 @@ ** - `#pragma` is in place for adding "meta-commands", but it does not ** yet have any concrete list of documented commands. ** -* - `#stderr` outputs its file name, line number, and the remainder +** - `#savepoint` takes one argument: begin, commit, rollback. Each +** corresponds to the similarly-named SQLite savepoint feature. +** (What we're calling "commit" is called "release" in savepoint +** terminology.) +** +** - `#stderr` outputs its file name, line number, and the remainder ** of that line to stderr. ** ** - `#//` acts as a single-line comment, noting that there must be no @@ -453,6 +458,7 @@ TT_IfNot, TT_Include, TT_Line, TT_Pragma, +TT_Savepoint, TT_Stderr, TT_Undef }; @@ -596,11 +602,15 @@ enum AtPolicy { }; typedef enum AtPolicy AtPolicy; -static AtPolicy AtPolicy_fromStr(char const *z){ +static AtPolicy AtPolicy_fromStr(char const *z, int bEnforce){ if( 0==strcmp(z, "retain") ) return AT_RETAIN; if( 0==strcmp(z, "elide") ) return AT_ELIDE; if( 0==strcmp(z, "error") ) return AT_ERROR; if( 0==strcmp(z, "off") ) return AT_OFF; + if( bEnforce ){ + fatal("Invalid @ policy value: %s. " + "Try one of retain|elide|error|off.", z); + } return AT_invalid; } @@ -633,16 +643,29 @@ static struct Global { const unsigned char chAt; } delim; struct { - sqlite3_stmt * defIns; - sqlite3_stmt * defDel; - sqlite3_stmt * defHas; - sqlite3_stmt * defGet; - sqlite3_stmt * defGetBool; - sqlite3_stmt * inclIns; - sqlite3_stmt * inclDel; - sqlite3_stmt * inclHas; - sqlite3_stmt * inclPathAdd; - sqlite3_stmt * inclSearch; +#define GStmt_map(E) \ + E(defIns,"INSERT OR REPLACE INTO def(k,v) VALUES(?,?)") \ + E(defDel,"DELETE FROM def WHERE k GLOB ?") \ + E(defHas,"SELECT 1 FROM def WHERE k GLOB ?") \ + E(defGet,"SELECT k,v FROM def WHERE k GLOB ?") \ + E(defGetBool, \ + "SELECT 1 FROM def WHERE k = ?1" \ + " AND v IS NOT NULL" \ + " AND '0'!=v AND ''!=v") \ + E(inclIns,"INSERT OR FAIL INTO incl(file,srcFile,srcLine) VALUES(?,?,?)") \ + E(inclDel,"DELETE FROM incl WHERE file=?") \ + E(inclHas,"SELECT 1 FROM incl WHERE file=?") \ + E(inclPathAdd,"INSERT OR FAIL INTO inclpath(seq,dir) VALUES(?,?)") \ + E(inclSearch, \ + "SELECT ?1 fn WHERE fileExists(fn) " \ + "UNION ALL SELECT * FROM (" \ + "SELECT replace(dir||'/'||?1, '//','/') AS fn " \ + "FROM inclpath WHERE fileExists(fn) ORDER BY seq" \ + ")") + +#define E(N,S) sqlite3_stmt * N; + GStmt_map(E) +#undef E } stmt; struct { FILE * pFile; @@ -688,6 +711,37 @@ static struct Global { } }; +/** Distinct IDs for each g.stmt member. */ +enum GStmt_e { + GStmt_e_none = 0, +#define E(N,S) GStmt_e_ ## N, + GStmt_map(E) +#undef E +}; + +/* +** Returns the g.stmt.X corresponding to `which`, initializing it if +** needed. It does not return NULL - it fails fatally on error. +*/ +static sqlite3_stmt * g_stmt(enum GStmt_e which){ + sqlite3_stmt ** q = 0; + char const * zSql = 0; + switch(which){ + case GStmt_e_none: + fatal("GStmt_e_none is not a valid statement handle"); + return NULL; +#define E(N,S) case GStmt_e_ ## N: zSql = S; q = &g.stmt.N; break; + GStmt_map(E) +#undef E + } + assert( q ); + assert( zSql && *zSql ); + if( !*q ){ + db_prepare(q, "%s", zSql); + assert( *q ); + } + return *q; +} #if 0 /* @@ -868,20 +922,13 @@ void db_free(void *m){ sqlite3_free(m); } -static sqlite3_stmt * db__prep_add(void){ - if(!g.stmt.defIns){ - db_prepare(&g.stmt.defIns, - "INSERT OR REPLACE INTO def(k,v) VALUES(?,?)"); - } - return g.stmt.defIns; -} - void db_define_add(const char * zKey){ int rc; char const * zEq = 0; unsigned nVal = 0; char const * zVal = cmpp_val_part(zKey, -1, '=', &nVal, &zEq); - sqlite3_stmt * const q = db__prep_add(); + sqlite3_stmt * const q = g_stmt(GStmt_e_defIns); + assert( q ); //g_stderr("zKey=%s\nzVal=%s\nzEq=%s\n", zKey, zVal, zEq); if( zEq ){ db_bind_textn(q, 1, zKey, (zEq-zKey)); @@ -920,7 +967,7 @@ static void db_define_add_file(const char * zKey){ FileWrapper fw = FileWrapper_empty; FileWrapper_open(&fw, zVal, "r"); FileWrapper_slurp(&fw); - q = db__prep_add(); + q = g_stmt(GStmt_e_defIns); //g_stderr("zKey=%s\nzVal=%s\nzEq=%s\n", zKey, zVal, zEq); db_bind_textn(q, 1, zKey, (zEq-zKey)); if( g.flags.chompF ){ @@ -953,11 +1000,7 @@ static void db_define_add_file(const char * zKey){ int db_define_has(const char * zName){ int rc; - sqlite3_stmt * q = g.stmt.defHas; - if(!q){ - db_prepare(&q, "SELECT 1 FROM def WHERE k GLOB ?"); - g.stmt.defHas = q; - } + sqlite3_stmt * const q = g_stmt(GStmt_e_defHas); db_bind_text(q, 1, zName); rc = db_step(q); if(SQLITE_ROW == rc){ @@ -973,17 +1016,8 @@ int db_define_has(const char * zName){ } int db_define_get_bool(const char * zName, int nName){ - sqlite3_stmt * q = g.stmt.defGetBool; + sqlite3_stmt * const q = g_stmt(GStmt_e_defGetBool); int rc = 0; - if(!q){ - db_prepare( - &q, - "SELECT 1 FROM def WHERE k = ?1" - " AND v IS NOT NULL" - " AND '0'!=v AND ''!=v" - ); - g.stmt.defGetBool = q; - } if( nName<0 ) nName=(int)strlen(zName); db_bind_textn(q, 1, zName, nName); rc = db_step(q); @@ -1003,21 +1037,16 @@ int db_define_get_bool(const char * zName, int nName){ int db_define_get(const char * zName, int nName, char **zVal, unsigned int *nVal){ - sqlite3_stmt * q = g.stmt.defGet; - int rc = 0; - int n = 0; - if(!q){ - db_prepare(&q, "SELECT k,v FROM def WHERE k GLOB ?"); - g.stmt.defGet = q; - } + sqlite3_stmt * q = g_stmt(GStmt_e_defGet); if( nName<0 ) nName=(int)strlen(zName); db_bind_textn(q, 1, zName, nName); - rc = db_step(q); + int n = 0; + int rc = db_step(q); if(SQLITE_ROW == rc){ const unsigned char * z = sqlite3_column_text(q, 1); n = sqlite3_column_bytes(q,1); if( nVal ) *nVal = (unsigned)n; - *zVal = n ? sqlite3_mprintf("%.*s", n, z) : 0; + *zVal = sqlite3_mprintf("%.*s", n, z); if( n && z ) check__oom(*zVal); if( SQLITE_ROW==sqlite3_step(q) ){ db_free(*zVal); @@ -1042,80 +1071,64 @@ int db_define_get(const char * zName, int nName, void db_define_rm(const char * zKey){ int rc; int n = 0; - if(!g.stmt.defDel){ - db_prepare(&g.stmt.defDel, "DELETE FROM def WHERE k GLOB ?"); - } - db_bind_text(g.stmt.defDel, 1, zKey); - rc = db_step(g.stmt.defDel); + sqlite3_stmt * const q = g_stmt(GStmt_e_defDel); + db_bind_text(q, 1, zKey); + rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping DELETE on def"); } g_debug(2,("undefine: %.*s\n",n, zKey)); - sqlite3_clear_bindings(g.stmt.defDel); - sqlite3_reset(g.stmt.defDel); + sqlite3_clear_bindings(q); + sqlite3_reset(q); } void db_including_add(const char * zKey, const char * zSrc, int srcLine){ int rc; - if(!g.stmt.inclIns){ - db_prepare(&g.stmt.inclIns, - "INSERT OR FAIL INTO incl(file,srcFile,srcLine) VALUES(?,?,?)"); - } - db_bind_text(g.stmt.inclIns, 1, zKey); - db_bind_text(g.stmt.inclIns, 2, zSrc); - db_bind_int(g.stmt.inclIns, 3, srcLine); - rc = db_step(g.stmt.inclIns); + sqlite3_stmt * const q = g_stmt(GStmt_e_inclIns); + db_bind_text(q, 1, zKey); + db_bind_text(q, 2, zSrc); + db_bind_int(q, 3, srcLine); + rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping INSERT on incl"); } g_debug(2,("is-including-file add [%s] from [%s]:%d\n", zKey, zSrc, srcLine)); - sqlite3_clear_bindings(g.stmt.inclIns); - sqlite3_reset(g.stmt.inclIns); + sqlite3_clear_bindings(q); + sqlite3_reset(q); } void db_include_rm(const char * zKey){ int rc; - if(!g.stmt.inclDel){ - db_prepare(&g.stmt.inclDel, "DELETE FROM incl WHERE file=?"); - } - db_bind_text(g.stmt.inclDel, 1, zKey); - rc = db_step(g.stmt.inclDel); + sqlite3_stmt * const q = g_stmt(GStmt_e_inclDel); + db_bind_text(q, 1, zKey); + rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping DELETE on incl"); } g_debug(2,("inclpath rm [%s]\n", zKey)); - sqlite3_clear_bindings(g.stmt.inclDel); - sqlite3_reset(g.stmt.inclDel); + sqlite3_clear_bindings(q); + sqlite3_reset(q); } char * db_include_search(const char * zKey){ char * zName = 0; - if(!g.stmt.inclSearch){ - db_prepare(&g.stmt.inclSearch, - "SELECT ?1 fn WHERE fileExists(fn) " - "UNION ALL SELECT * FROM (" - "SELECT replace(dir||'/'||?1, '//','/') AS fn " - "FROM inclpath WHERE fileExists(fn) ORDER BY seq" - ")"); - } - db_bind_text(g.stmt.inclSearch, 1, zKey); - if(SQLITE_ROW==db_step(g.stmt.inclSearch)){ - const unsigned char * z = sqlite3_column_text(g.stmt.inclSearch, 0); + sqlite3_stmt * const q = g_stmt(GStmt_e_inclSearch); + db_bind_text(q, 1, zKey); + if(SQLITE_ROW==db_step(q)){ + const unsigned char * z = sqlite3_column_text(q, 0); zName = z ? sqlite3_mprintf("%s", z) : 0; if(!zName) fatal("Alloc failed"); } - sqlite3_clear_bindings(g.stmt.inclSearch); - sqlite3_reset(g.stmt.inclSearch); + sqlite3_clear_bindings(q); + sqlite3_reset(q); return zName; } static int db_including_has(const char * zName){ int rc; - if(!g.stmt.inclHas){ - db_prepare(&g.stmt.inclHas, "SELECT 1 FROM incl WHERE file=?"); - } - db_bind_text(g.stmt.inclHas, 1, zName); - rc = db_step(g.stmt.inclHas); + sqlite3_stmt * const q = g_stmt(GStmt_e_inclHas); + db_bind_text(q, 1, zName); + rc = db_step(q); if(SQLITE_ROW == rc){ rc = 1; }else{ @@ -1123,8 +1136,8 @@ static int db_including_has(const char * zName){ rc = 0; } g_debug(2,("inclpath has [%s] = %d\n",zName, rc)); - sqlite3_clear_bindings(g.stmt.inclHas); - sqlite3_reset(g.stmt.inclHas); + sqlite3_clear_bindings(q); + sqlite3_reset(q); return rc; } @@ -1143,28 +1156,22 @@ void db_including_check(const char * zName){ void db_include_dir_add(const char * zDir){ static int seq = 0; int rc; - if(!g.stmt.inclPathAdd){ - db_prepare(&g.stmt.inclPathAdd, - "INSERT OR FAIL INTO inclpath(seq,dir) VALUES(?,?)"); - } - db_bind_int(g.stmt.inclPathAdd, 1, ++seq); - db_bind_text(g.stmt.inclPathAdd, 2, zDir); - rc = db_step(g.stmt.inclPathAdd); + sqlite3_stmt * const q = g_stmt(GStmt_e_inclPathAdd); + db_bind_int(q, 1, ++seq); + db_bind_text(q, 2, zDir); + rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping INSERT on inclpath"); } g_debug(2,("inclpath add #%d: %s\n",seq, zDir)); - sqlite3_clear_bindings(g.stmt.inclPathAdd); - sqlite3_reset(g.stmt.inclPathAdd); + sqlite3_clear_bindings(q); + sqlite3_reset(q); } static void cmpp_atexit(void){ -#define FINI(M) if(g.stmt.M) sqlite3_finalize(g.stmt.M) - FINI(defIns); FINI(defDel); - FINI(defHas); FINI(defGet); FINI(defGetBool); - FINI(inclIns); FINI(inclDel); FINI(inclHas); - FINI(inclPathAdd); FINI(inclSearch); -#undef FINI +#define E(N,S) sqlite3_finalize(g.stmt.N); + GStmt_map(E) +#undef E FileWrapper_close(&g.out); if(g.db) sqlite3_close(g.db); } @@ -1195,8 +1202,8 @@ static int cmpp__db_sq3TraceV2(unsigned t,void*c,void*p,void*x){ case SQLITE_TRACE_STMT:{ FILE * const fp = g.sqlTrace.pFile; if( fp ){ - char const * zSql = (char const *)x; - char * zExp = g.sqlTrace.expandSql + char const * const zSql = (char const *)x; + char * const zExp = g.sqlTrace.expandSql ? sqlite3_expanded_sql((sqlite3_stmt*)p) : 0; fprintf(fp, "SQL TRACE #%u: %s\n", @@ -1230,6 +1237,7 @@ static void cmpp_initdb(void){ "seq INTEGER UNIQUE ON CONFLICT IGNORE, " "dir TEXT PRIMARY KEY NOT NULL ON CONFLICT IGNORE" ");" + "BEGIN;" ; assert(0==g.db); if(g.db) return; @@ -1723,8 +1731,8 @@ static void cmpp_kwd_include(CmppKeyword const * pKw, CmppTokenizer *t){ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ const char * zArg; if(CT_skip(t)) return; - else if(t->args.argc!=2){ - cmpp_kwd__misuse(pKw, t, "Expecting one argument"); + else if(t->args.argc<2){ + cmpp_kwd__misuse(pKw, t, "Expecting an argument"); } zArg = (const char *)t->args.argv[1]; #define M(X) 0==strcmp(zArg,X) @@ -1733,13 +1741,18 @@ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ db_prepare(&q, "SELECT k FROM def ORDER BY k"); g_stderr("cmpp defines:\n"); while(SQLITE_ROW==db_step(q)){ - int const n = sqlite3_column_bytes(q, 0); const char * z = (const char *)sqlite3_column_text(q, 0); + int const n = sqlite3_column_bytes(q, 0); g_stderr("\t%.*s\n", n, z); } db_finalize(q); }else if(M("@")){ - g.flags.atPolicy = AT_RETAIN; + if(t->args.argc>2){ + g.flags.atPolicy = + AtPolicy_fromStr((char const *)t->args.argv[2], 1); + }else{ + g.flags.atPolicy = AT_DEFAULT; + } }else if(M("no-@")){ g.flags.atPolicy = AT_OFF; }else if(M("chomp-F")){ @@ -1752,6 +1765,41 @@ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ #undef M } +/* Impl. for #savepoint. */ +static void cmpp_kwd_savepoint(CmppKeyword const * pKw, CmppTokenizer *t){ + const char * zArg; + if(CT_skip(t)) return; + else if(t->args.argc!=2){ + cmpp_kwd__misuse(pKw, t, "Expecting one argument"); + } + zArg = (const char *)t->args.argv[1]; +#define SP_NAME " cmpp /*" __FILE__ "*/;" +#define M(X) 0==strcmp(zArg,X) + if(M("begin")){ + db_affirm_rc(sqlite3_exec(g.db, "SAVEPOINT" SP_NAME, 0, 0, 0), + "Starting a savepoint"); + }else if(M("rollback")){ + db_affirm_rc( + sqlite3_exec(g.db, + "ROLLBACK TO SAVEPOINT" SP_NAME + "RELEASE SAVEPOINT" SP_NAME, + 0, 0, 0 + ), "Rolling back a savepoint" + ); + }else if(M("commit")){ + db_affirm_rc( + sqlite3_exec(g.db, + "RELEASE" SP_NAME, + 0, 0, 0 + ), "Committing a savepoint" + ); + }else{ + cmpp_kwd__misuse(pKw, t, "Unknown savepoint option: %s", zArg); + } +#undef SP_NAME +#undef M +} + /* #stder impl. */ static void cmpp_kwd_stderr(CmppKeyword const * pKw, CmppTokenizer *t){ if(CT_skip(t)) return; @@ -1789,6 +1837,7 @@ CmppKeyword aKeywords[] = { {"ifnot", 5, 1, TT_IfNot, cmpp_kwd_if}, {"include", 7, 0, TT_Include, cmpp_kwd_include}, {"pragma", 6, 1, TT_Pragma, cmpp_kwd_pragma}, + {"savepoint", 9, 1, TT_Savepoint, cmpp_kwd_savepoint}, {"stderr", 6, 0, TT_Stderr, cmpp_kwd_stderr}, {"undef", 5, 1, TT_Undef, cmpp_kwd_define}, {0,0,TT_Invalid, 0} @@ -2053,10 +2102,7 @@ int main(int argc, char const * const * argv){ ISFLAG("@-policy"){ AtPolicy aup; ARGVAL; - aup = AtPolicy_fromStr(zVal); - if( AT_invalid==aup ){ - fatal("Invalid --@-policy. Try one of retain|elide|error|off."); - } + aup = AtPolicy_fromStr(zVal, 1); DOIT { g.flags.atPolicy = aup; } diff --git a/manifest b/manifest index d1d776feb9..083b1bedf9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\ssqlite3_malloc64()\sand\ssqlite3_realloc64()\severywhere\sin\sthe\sCLI. -D 2025-09-27T13:00:37.088 +C ext/wasm/c-pp.c:\sadd\s#savepoint\ssupport.\sConsolidate\show\sthe\sdiverse\ssqlite3_stmt\shandles\sare\smanaged. +D 2025-09-27T13:34:02.718 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -610,7 +610,7 @@ F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72 F ext/wasm/api/sqlite3-wasm.c ff2dc011e17b06186b8b35e408626d7ace69a362b92c197a34d78bef25c7105a F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 8fb6adfbae6270344f43f2652e63780df3f86521755bde8f92cf6b809ba7891d F ext/wasm/api/sqlite3-worker1.c-pp.js 69483df1df2d0988e708390f7b1feda769c16e9e9efd4683557f8e7197099cc0 -F ext/wasm/c-pp.c 3c84267ad9c9018f8eb0c83952b1b37eac08e0c614d6771fba5cf7640641525b +F ext/wasm/c-pp.c c0c48f2d302d13d1c9f340aa8ace68b36de2cf5a9b4f6e9ce1c3b20e3554c46f F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f @@ -2169,8 +2169,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 865352499223c3bdc5cb01a2f984d6e3fe74a028ab6a0cf2d92fec49383633f6 -R 8bd4bde493e0eb54d65eb66f6ba457ae -U drh -Z c33262b9d6d9f5e2eac9a7be85ed3b9e +P b05c47009120840f74955114082f3a9c1206a81bd935a503fc359b5bde61c996 +R 5c9bc3e1c5ff8428ad8c5e411548be45 +U stephan +Z 0c4774a9e741ee60e5e425d698f46673 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 971d62333e..bf59fd1bbb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b05c47009120840f74955114082f3a9c1206a81bd935a503fc359b5bde61c996 +b44650f907e9cb4ec908bb7525488e309946fac9d84cdac4cdde730527a440a9 -- 2.47.3