1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-01-06 08:01:16 +03:00

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
This commit is contained in:
stephan
2022-10-03 13:03:41 +00:00
parent a4c357f94c
commit 4f5bbedb3a
10 changed files with 122 additions and 44 deletions

View File

@@ -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

View File

@@ -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])){

View File

@@ -61,6 +61,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
@@ -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,24 +149,48 @@ 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,
@@ -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);

View File

@@ -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();

View File

@@ -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"],

View File

@@ -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);
@@ -680,6 +687,28 @@ int sqlite3_wasm_vfs_unlink(const char * zName){
return rc;
}
/*
** 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