aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api
diff options
context:
space:
mode:
authorstephan <stephan@noemail.net>2022-10-03 13:03:41 +0000
committerstephan <stephan@noemail.net>2022-10-03 13:03:41 +0000
commit4f5bbedb3aad0095caef387dc66e884bfc1f1675 (patch)
tree58445002e5c48f8151a1ea20dbed5d8f29ebf8cb /ext/wasm/api
parenta4c357f94c8c5408c18e1f020a6267985c889254 (diff)
downloadsqlite-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-api1
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js2
-rw-r--r--ext/wasm/api/sqlite3-api-oo1.js88
-rw-r--r--ext/wasm/api/sqlite3-api-opfs.js9
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js1
-rw-r--r--ext/wasm/api/sqlite3-wasm.c29
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