diff options
author | stephan <stephan@noemail.net> | 2022-10-03 13:03:41 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2022-10-03 13:03:41 +0000 |
commit | 4f5bbedb3aad0095caef387dc66e884bfc1f1675 (patch) | |
tree | 58445002e5c48f8151a1ea20dbed5d8f29ebf8cb /ext/wasm/api | |
parent | a4c357f94c8c5408c18e1f020a6267985c889254 (diff) | |
download | sqlite-4f5bbedb3aad0095caef387dc66e884bfc1f1675.tar.gz sqlite-4f5bbedb3aad0095caef387dc66e884bfc1f1675.zip |
Export sqlite3_trace_v2() to wasm and use it to ensure that the new per-VFS post-open SQL support in the DB ctor works. Default opfs vfs to journal_mode=truncate, as it's faster in that mode. Add 't' DB open-mode flag to enable SQL tracing to console.log().
FossilOrigin-Name: 508f7f6d63e52f61fae5abe817579a4e130fa7fbd18733d741d521a5bdabb7ce
Diffstat (limited to 'ext/wasm/api')
-rw-r--r-- | ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 1 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.js | 2 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-oo1.js | 88 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-opfs.js | 9 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 1 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 29 |
6 files changed, 102 insertions, 28 deletions
diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 014812946..506775d2f 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -74,6 +74,7 @@ _sqlite3_strglob _sqlite3_strlike _sqlite3_total_changes _sqlite3_total_changes64 +_sqlite3_trace_v2 _sqlite3_uri_boolean _sqlite3_uri_int64 _sqlite3_uri_key diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index e31618d5e..a4c9627bd 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -621,7 +621,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ for(const t of ['access', 'blobFinalizers', 'dataTypes', 'encodings', 'fcntl', 'flock', 'ioCap', 'openFlags', 'prepareFlags', 'resultCodes', - 'serialize', 'syncFlags', 'udfFlags', + 'serialize', 'syncFlags', 'trace', 'udfFlags', 'version' ]){ for(const e of Object.entries(wasm.ctype[t])){ diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index e6685d35a..37c292840 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -62,6 +62,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; /** + sqlite3_trace_v2() callback which gets installed by the DB ctor + if its open-flags contain "t". + */ + const __dbTraceToConsole = + wasm.installFunction('i(ippp)', function(t,c,p,x){ + if(capi.SQLITE_TRACE_STMT===t){ + // x == SQL, p == sqlite3_stmt* + console.log("SQL TRACE #"+(++this.counter), + wasm.cstringToJs(x)); + } + }.bind({counter: 0})); + + /** + A map of sqlite3_vfs pointers to SQL code to run when the DB + constructor opens a database with the given VFS. + */ + const __vfsPostOpenSql = Object.create(null); + + /** A proxy for DB class constructors. It must be called with the being-construct DB object as its "this". See the DB constructor for the argument docs. This is split into a separate function @@ -101,12 +120,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ? (n)=>toss3("The VFS for",n,"is only available in the main window thread.") : false; ctor._name2vfs[':localStorage:'] = { - vfs: 'kvvfs', - filename: isWorkerThread || (()=>'local') + vfs: 'kvvfs', filename: isWorkerThread || (()=>'local') }; ctor._name2vfs[':sessionStorage:'] = { - vfs: 'kvvfs', - filename: isWorkerThread || (()=>'session') + vfs: 'kvvfs', filename: isWorkerThread || (()=>'session') }; } const opt = ctor.normalizeArgs(...args); @@ -123,7 +140,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ vfsName = vfsCheck.vfs; fn = fnJs = vfsCheck.filename(fnJs); } - let ptr, oflags = 0; + let pDb, oflags = 0; if( flagsStr.indexOf('c')>=0 ){ oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; } @@ -132,25 +149,49 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ oflags |= capi.SQLITE_OPEN_EXRESCODE; const scope = wasm.scopedAllocPush(); try { - const ppDb = wasm.allocPtr() /* output (sqlite3**) arg */; + const pPtr = wasm.allocPtr() /* output (sqlite3**) arg */; const pVfsName = vfsName ? ( ('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName)) ): 0; - const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName); - ptr = wasm.getPtrValue(ppDb); - checkSqlite3Rc(ptr, rc); + let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, pVfsName); + pDb = wasm.getPtrValue(pPtr); + checkSqlite3Rc(pDb, rc); + if(flagsStr.indexOf('t')>=0){ + capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, + __dbTraceToConsole, 0); + } + // Check for per-VFS post-open SQL... + wasm.setPtrValue(pPtr, 0); + if(0===capi.sqlite3_file_control( + pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, pPtr + )){ + const postInitSql = __vfsPostOpenSql[wasm.getPtrValue(pPtr)]; + if(postInitSql){ + rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0); + checkSqlite3Rc(pDb, rc); + } + } }catch( e ){ - if( ptr ) capi.sqlite3_close_v2(ptr); + if( pDb ) capi.sqlite3_close_v2(pDb); throw e; }finally{ wasm.scopedAllocPop(scope); } this.filename = fnJs; - __ptrMap.set(this, ptr); + __ptrMap.set(this, pDb); __stmtMap.set(this, Object.create(null)); }; /** + Sets SQL which should be exec()'d on a DB instance after it is + opened with the given VFS pointer. This is intended only for use + by DB subclasses or sqlite3_vfs implementations. + */ + dbCtorHelper.setVfsPostOpenSql = function(pVfs, sql){ + __vfsPostOpenSql[pVfs] = sql; + }; + + /** A helper for DB constructors. It accepts either a single config-style object or up to 3 arguments (filename, dbOpenFlags, dbVfsName). It returns a new object containing: @@ -175,7 +216,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return arg; }; - /** The DB class provides a high-level OO wrapper around an sqlite3 db handle. @@ -193,14 +233,18 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ database. It must be string containing a sequence of letters (in any order, but case sensitive) specifying the mode: - - "c" => create if it does not exist, else fail if it does not + - "c": create if it does not exist, else fail if it does not exist. Implies the "w" flag. - - "w" => write. Implies "r": a db cannot be write-only. + - "w": write. Implies "r": a db cannot be write-only. - - "r" => read-only if neither "w" nor "c" are provided, else it + - "r": read-only if neither "w" nor "c" are provided, else it is ignored. + - "t": enable tracing of SQL executed on this database handle, + sending it to `console.log()`. Once enabled, it cannot + currently be easily switched off (TODO). + If "w" is not provided, the db is implicitly read-only, noting that "rc" is meaningless @@ -229,16 +273,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ `sqlite3*` pointer value. That property can also be used to check whether this DB instance is still open. - - EXPERIMENTAL: in the main window thread, the filenames - ":localStorage:" and ":sessionStorage:" are special: they cause - the db to use either localStorage or sessionStorage for storing - the database. In this mode, only a single database is permitted - in each storage object. This feature is experimental and subject - to any number of changes (including outright removal). This - support requires the kvvfs sqlite3 VFS, the existence of which - can be determined at runtime by checking for a non-0 return value - from sqlite3.capi.sqlite3_vfs_find("kvvfs"). + In the main window thread, the filenames ":localStorage:" and + ":sessionStorage:" are special: they cause the db to use either + localStorage or sessionStorage for storing the database using + the kvvfs. */ const DB = function(...args){ dbCtorHelper.apply(this, args); diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 9588fc8a2..42c0e1166 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -898,8 +898,14 @@ const installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ sqlite3.oo1.dbCtorHelper.call(this, opt); }; opfsUtil.OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + sqlite3.oo1.dbCtorHelper.setVfsPostOpenSql( + opfsVfs.pointer, + /* Truncate journal mode is faster than delete or wal for + OPFS, per speedtest1. */ + "pragma journal_mode=truncate" + ); } - + /** Potential TODOs: @@ -907,7 +913,6 @@ const installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ publish an interface for proxying the higher-level OPFS features like getting a directory listing. */ - const sanityCheck = function(){ const scope = wasm.scopedAllocPush(); const sq3File = new sqlite3_file(); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 83e47cc93..377f671fc 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -815,6 +815,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_step", "int", "sqlite3_stmt*"], ["sqlite3_strglob", "int", "string","string"], ["sqlite3_strlike", "int", "string","string","int"], + ["sqlite3_trace_v2", "int", "sqlite3*", "int", "*", "*"], ["sqlite3_total_changes", "int", "sqlite3*"], ["sqlite3_uri_boolean", "int", "string", "string", "int"], ["sqlite3_uri_key", "string", "string", "int"], diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index b9454155d..99196db15 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -527,6 +527,13 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_SYNC_DATAONLY); } _DefGroup; + DefGroup(trace) { + DefInt(SQLITE_TRACE_STMT); + DefInt(SQLITE_TRACE_PROFILE); + DefInt(SQLITE_TRACE_ROW); + DefInt(SQLITE_TRACE_CLOSE); + } _DefGroup; + DefGroup(udfFlags) { DefInt(SQLITE_DETERMINISTIC); DefInt(SQLITE_DIRECTONLY); @@ -681,6 +688,28 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ } /* +** This function is NOT part of the sqlite3 public API. It is strictly +** for use by the sqlite project's own JS/WASM bindings. +** +** This function resets the given db pointer's database as described at +** +** https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigresetdatabase +** +** Returns 0 on success, an SQLITE_xxx code on error. Returns +** SQLITE_MISUSE if pDb is NULL. +*/ +WASM_KEEP +int sqlite3_wasm_db_reset(sqlite3*pDb){ + int rc = SQLITE_MISUSE; + if( pDb ){ + rc = sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); + if( 0==rc ) rc = sqlite3_exec(pDb, "VACUUM", 0, 0, 0); + sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); + } + return rc; +} + +/* ** Uses the current database's VFS xRead to stream the db file's ** contents out to the given callback. The callback gets a single ** chunk of size n (its 2nd argument) on each call and must return 0 |