_sqlite3_create_function_v2
_sqlite3_data_count
_sqlite3_db_filename
+_sqlite3_db_handle
_sqlite3_db_name
_sqlite3_errmsg
_sqlite3_error_offset
_sqlite3_expanded_sql
_sqlite3_extended_errcode
_sqlite3_extended_result_codes
+_sqlite3_file_control
_sqlite3_finalize
_sqlite3_initialize
_sqlite3_interrupt
/**
Install a suitable default configuration for sqlite3ApiBootstrap().
*/
- const SABC = self.sqlite3ApiBootstrap.defaultConfig;
- SABC.Module = Module /* ==> Currently needs to be exposed here for test code. NOT part
- of the public API. */;
- SABC.exports = Module['asm'];
- SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
+ const SABC = self.sqlite3ApiConfig || Object.create(null);
+ if(undefined===SABC.Module){
+ SABC.Module = Module /* ==> Currently needs to be exposed here for
+ test code. NOT part of the public API. */;
+ }
+ if(undefined===SABC.exports){
+ SABC.exports = Module['asm'];
+ }
+ if(undefined===SABC.memory){
+ SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
+ }
/**
For current (2022-08-22) purposes, automatically call
configuration used by a no-args call to sqlite3ApiBootstrap().
*/
//console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig);
- const sqlite3 = self.sqlite3ApiBootstrap();
- delete self.sqlite3ApiBootstrap;
+ const rmApiConfig = (SABC !== self.sqlite3ApiConfig);
+ self.sqlite3ApiConfig = SABC;
+ let sqlite3;
+ try{
+ sqlite3 = self.sqlite3ApiBootstrap();
+ }finally{
+ delete self.sqlite3ApiBootstrap;
+ if(rmApiConfig) delete self.sqlite3ApiConfig;
+ }
if(self.location && +self.location.port > 1024){
console.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
*/
const aPtr = wasm.xWrap.argAdapter('*');
wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
+ wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
/**
Populate api object with sqlite3_...() by binding the "raw" wasm
wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
//console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
for(const t of ['access', 'blobFinalizers', 'dataTypes',
- 'encodings', 'flock', 'ioCap',
+ 'encodings', 'fcntl', 'flock', 'ioCap',
'openFlags', 'prepareFlags', 'resultCodes',
'syncFlags', 'udfFlags', 'version'
]){
the statement actually produces any result rows.
==================================================================
+ - `.columnNames`: if this is an array, the column names of the
+ result set are stored in this array before the callback (if
+ any) is triggered (regardless of whether the query produces any
+ result rows). If no statement has result columns, this value is
+ unchanged. Achtung: an SQL result may have multiple columns
+ with identical names.
+
- `.callback` = a function which gets called for each row of
the result set, but only if that statement has any result
_rows_. The callback's "this" is the options object. The second
as the first argument to the callback:
A.1) `'array'` (the default) causes the results of
- `stmt.get([])` to be passed to passed on and/or appended to
- `resultRows`.
+ `stmt.get([])` to be passed to the `callback` and/or appended
+ to `resultRows`.
A.2) `'object'` causes the results of
`stmt.get(Object.create(null))` to be passed to the
A.3) `'stmt'` causes the current Stmt to be passed to the
callback, but this mode will trigger an exception if
`resultRows` is an array because appending the statement to
- the array would be unhelpful.
+ the array would be downright unhelpful.
B) An integer, indicating a zero-based column in the result
row. Only that one single value will be passed on.
':', '$', or '@' will fetch the row as an object, extract that
one field, and pass that field's value to the callback. Note
that these keys are case-sensitive so must match the case used
- in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A'
- would work but '$a' would not. A reference to a column not in
- the result set will trigger an exception on the first row (as
- the check is not performed until rows are fetched).
+ in the SQL. e.g. `"select a A from t"` with a `rowMode` of
+ `'$A'` would work but `'$a'` would not. A reference to a column
+ not in the result set will trigger an exception on the first
+ row (as the check is not performed until rows are fetched).
+ Note also that `$` is a legal identifier character in JS so
+ need not be quoted. (Design note: those 3 characters were
+ chosen because they are the characters support for naming bound
+ parameters.)
Any other `rowMode` value triggers an exception.
and can be used over a WebWorker-style message interface.
exec() throws if `resultRows` is set and `rowMode` is 'stmt'.
- - `.columnNames`: if this is an array, the column names of the
- result set are stored in this array before the callback (if
- any) is triggered (regardless of whether the query produces any
- result rows). If no statement has result columns, this value is
- unchanged. Achtung: an SQL result may have multiple columns
- with identical names.
+
+ Potential TODOs:
+
+ - `.bind`: permit an array of arrays/objects to bind. The first
+ sub-array would act on the first statement which has bindable
+ parameters (as it does now). The 2nd would act on the next such
+ statement, etc.
+
+ - `.callback` and `.resultRows`: permit an array entries with
+ semantics similar to those described for `.bind` above.
+
*/
exec: function(/*(sql [,obj]) || (obj)*/){
affirmDbOpen(this);
Stmt
}/*oo1 object*/;
- if( self.window===self && 0!==capi.sqlite3_vfs_find('kvvfs') ){
- /* Features specific to kvvfs... */
- /**
- Clears all storage used by the kvvfs DB backend, deleting any
- DB(s) stored there. Its argument must be either 'session',
- 'local', or ''. In the first two cases, only sessionStorage
- resp. localStorage is cleared. If it's an empty string (the
- default) then both are cleared. Only storage keys which match
- the pattern used by kvvfs are cleared: any other client-side
- data are retained.
- */
- DB.clearKvvfsStorage = function(which=''){
- const prefix = 'kvvfs-'+which;
- const stores = [];
- if('session'===which || ''===which) stores.push(sessionStorage);
- if('local'===which || ''===which) stores.push(localStorage);
- stores.forEach(function(s){
- const toRm = [];
- let i = 0, k;
- for( i = 0; (k = s.key(i)); ++i ){
- if(k.startsWith(prefix)) toRm.push(k);
- }
- toRm.forEach((kk)=>s.removeItem(kk));
- });
- };
- }/* main-window-only bits */
});
- Insofar as possible, support client-side storage using JS
filesystem APIs. As of this writing, such things are still very
- much TODO. Initial testing with using IndexedDB as backing storage
- showed it to work reasonably well, but it's also too easy to
- corrupt by using a web page in two browser tabs because IndexedDB
- lacks the locking features needed to support that.
+ much under development.
Specific non-goals of this project:
Encodings in that realm, there are no currently plans to support
the UTF16-related sqlite3 APIs. They would add a complication to
the bindings for no appreciable benefit. Though web-related
- implementation details take priority, the lower-level WASM module
- "should" work in non-web WASM environments.
+ implementation details take priority, and the JavaScript
+ components of the API specifically focus on browser clients, the
+ lower-level WASM module "should" work in non-web WASM
+ environments.
- Supporting old or niche-market platforms. WASM is built for a
modern web and requires modern platforms.
*/
/**
- sqlite3ApiBootstrap() is the only global symbol exposed by this
- API. It is intended to be called one time at the end of the API
- amalgamation process, passed configuration details for the current
- environment, and then optionally be removed from the global object
- using `delete self.sqlite3ApiBootstrap`.
+ sqlite3ApiBootstrap() is the only global symbol persistently
+ exposed by this API. It is intended to be called one time at the
+ end of the API amalgamation process, passed configuration details
+ for the current environment, and then optionally be removed from
+ the global object using `delete self.sqlite3ApiBootstrap`.
This function expects a configuration object, intended to abstract
away details specific to any given WASM environment, primarily so
that it can be used without any _direct_ dependency on
- Emscripten. The config object is only honored the first time this
- is called. Subsequent calls ignore the argument and return the same
+ Emscripten. (Note the default values for the config object!) The
+ config object is only honored the first time this is
+ called. Subsequent calls ignore the argument and return the same
(configured) object which gets initialized by the first call.
The config object properties include:
*/
'use strict';
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
- apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig)
+ apiConfig = (self.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig)
){
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
console.warn("sqlite3ApiBootstrap() called multiple times.",
) ? !!capi.sqlite3_compileoption_used(optName) : false;
}/*compileOptionUsed()*/;
+ /**
+ Signatures for the WASM-exported C-side functions. Each entry
+ is an array with 2+ elements:
+
+ [ "c-side name",
+ "result type" (capi.wasm.xWrap() syntax),
+ [arg types in xWrap() syntax]
+ // ^^^ this needn't strictly be an array: it can be subsequent
+ // elements instead: [x,y,z] is equivalent to x,y,z
+ ]
+
+ Note that support for the API-specific data types in the
+ result/argument type strings gets plugged in at a later phase in
+ the API initialization process.
+ */
capi.wasm.bindingSignatures = [
- /**
- Signatures for the WASM-exported C-side functions. Each entry
- is an array with 2+ elements:
-
- ["c-side name",
- "result type" (capi.wasm.xWrap() syntax),
- [arg types in xWrap() syntax]
- // ^^^ this needn't strictly be an array: it can be subsequent
- // elements instead: [x,y,z] is equivalent to x,y,z
- ]
- */
// Please keep these sorted by function name!
["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"],
["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
"sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"],
["sqlite3_data_count", "int", "sqlite3_stmt*"],
["sqlite3_db_filename", "string", "sqlite3*", "string"],
+ ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
["sqlite3_db_name", "string", "sqlite3*", "int"],
["sqlite3_errmsg", "string", "sqlite3*"],
["sqlite3_error_offset", "int", "sqlite3*"],
["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
["sqlite3_extended_errcode", "int", "sqlite3*"],
["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
+ ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"],
["sqlite3_finalize", "int", "sqlite3_stmt*"],
["sqlite3_initialize", undefined],
["sqlite3_interrupt", undefined, "sqlite3*"
/**
Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a
- non-empty string and the given name has that string as its
- prefix, else returns false.
+ non-empty string and the given name starts with (that string +
+ '/'), else returns false.
+
+ Potential (but arguable) TODO: return true if the name is one of
+ (":localStorage:", "local", ":sessionStorage:", "session") and
+ kvvfs is available.
*/
capi.sqlite3_web_filename_is_persistent = function(name){
const p = capi.sqlite3_web_persistent_dir();
- return (p && name) ? name.startsWith(p) : false;
+ return (p && name) ? name.startsWith(p+'/') : false;
};
-
+
if(0===capi.wasm.exports.sqlite3_vfs_find(0)){
/* Assume that sqlite3_initialize() has not yet been called.
This will be the case in an SQLITE_OS_KV build. */
capi.wasm.exports.sqlite3_initialize();
}
+ if( self.window===self ){
+ /* Features specific to the main window thread... */
+
+ /**
+ Internal helper for sqlite3_web_kvvfs_clear() and friends.
+ Its argument should be one of ('local','session','').
+ */
+ const __kvvfsInfo = function(which){
+ const rc = Object.create(null);
+ rc.prefix = 'kvvfs-'+which;
+ rc.stores = [];
+ if('session'===which || ''===which) rc.stores.push(self.sessionStorage);
+ if('local'===which || ''===which) rc.stores.push(self.localStorage);
+ return rc;
+ };
+
+ /**
+ Clears all storage used by the kvvfs DB backend, deleting any
+ DB(s) stored there. Its argument must be either 'session',
+ 'local', or ''. In the first two cases, only sessionStorage
+ resp. localStorage is cleared. If it's an empty string (the
+ default) then both are cleared. Only storage keys which match
+ the pattern used by kvvfs are cleared: any other client-side
+ data are retained.
+
+ This function is only available in the main window thread.
+
+ Returns the number of entries cleared.
+ */
+ capi.sqlite3_web_kvvfs_clear = function(which=''){
+ let rc = 0;
+ const kvinfo = __kvvfsInfo(which);
+ kvinfo.stores.forEach((s)=>{
+ const toRm = [] /* keys to remove */;
+ let i;
+ for( i = 0; i < s.length; ++i ){
+ const k = s.key(i);
+ if(k.startsWith(kvinfo.prefix)) toRm.push(k);
+ }
+ toRm.forEach((kk)=>s.removeItem(kk));
+ rc += toRm.length;
+ });
+ return rc;
+ };
+
+ /**
+ This routine guesses the approximate amount of
+ window.localStorage and/or window.sessionStorage in use by the
+ kvvfs database backend. Its argument must be one of
+ ('session', 'local', ''). In the first two cases, only
+ sessionStorage resp. localStorage is counted. If it's an empty
+ string (the default) then both are counted. Only storage keys
+ which match the pattern used by kvvfs are counted. The returned
+ value is the "length" value of every matching key and value,
+ noting that the kvvf uses only ASCII keys and values.
+
+ Note that the returned size is not authoritative from the
+ perspective of how much data can fit into localStorage and
+ sessionStorage, as the precise algorithms for determining
+ those limits are unspecified and may include per-entry
+ overhead invisible to clients.
+ */
+ capi.sqlite3_web_kvvfs_size = function(which=''){
+ let sz = 0;
+ const kvinfo = __kvvfsInfo(which);
+ kvinfo.stores.forEach((s)=>{
+ let i;
+ for(i = 0; i < s.length; ++i){
+ const k = s.key(i);
+ if(k.startsWith(kvinfo.prefix)){
+ sz += k.length;
+ sz += s.getItem(k).length;
+ }
+ }
+ });
+ return sz;
+ };
+
+ /**
+ Given an `sqlite3*`, returns a truthy value (see below) if that
+ db handle uses the "kvvfs" VFS, else returns false. If pDb is
+ NULL then this function returns true if the default VFS is
+ "kvvfs". Results are undefined if pDb is truthy but refers to
+ an invalid pointer.
+
+ The truthy value it returns is a pointer to the kvvfs
+ `sqlite3_vfs` object.
+ */
+ capi.sqlite3_web_db_is_kvvfs = function(pDb){
+ const pK = capi.sqlite3_vfs_find("kvvfs");
+ if(!pK) return false;
+ else if(!pDb){
+ return capi.sqlite3_vfs_find(0) && pK;
+ }
+ const scope = capi.wasm.scopedAllocPush();
+ try{
+ const ppVfs = capi.wasm.scopedAllocPtr();
+ return (
+ (0===capi.sqlite3_file_control(
+ pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
+ )) && (capi.wasm.getPtrValue(ppVfs) === pK)
+ ) ? pK : false;
+ }finally{
+ capi.wasm.scopedAllocPop(scope);
+ }
+ };
+ }/* main-window-only bits */
+
/* The remainder of the API will be set up in later steps. */
const sqlite3 = {
WasmAllocError: WasmAllocError,
global-scope symbol.
*/
self.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
-/** Placeholder: gets installed by the first call to
- self.sqlite3ApiBootstrap(). */
+/**
+ Placeholder: gets installed by the first call to
+ self.sqlite3ApiBootstrap(). However, it is recommended that the
+ caller of sqlite3ApiBootstrap() capture its return value and delete
+ self.sqlite3ApiBootstrap after calling it. It returns the same
+ value which will be stored here.
+*/
self.sqlite3ApiBootstrap.sqlite3 = undefined;
*/
WASM_KEEP
const char * sqlite3_wasm_enum_json(void){
- static char strBuf[1024 * 8] = {0} /* where the JSON goes */;
+ static char strBuf[1024 * 12] = {0} /* where the JSON goes */;
int n = 0, childCount = 0, structCount = 0
/* output counters for figuring out where commas go */;
char * pos = &strBuf[1] /* skip first byte for now to help protect
} _DefGroup;
DefGroup(openFlags) {
- /* Noting that not all of these will have any effect in WASM-space. */
+ /* Noting that not all of these will have any effect in
+ ** WASM-space. */
DefInt(SQLITE_OPEN_READONLY);
DefInt(SQLITE_OPEN_READWRITE);
DefInt(SQLITE_OPEN_CREATE);
DefInt(SQLITE_IOCAP_BATCH_ATOMIC);
} _DefGroup;
+ DefGroup(fcntl) {
+ DefInt(SQLITE_FCNTL_LOCKSTATE);
+ DefInt(SQLITE_FCNTL_GET_LOCKPROXYFILE);
+ DefInt(SQLITE_FCNTL_SET_LOCKPROXYFILE);
+ DefInt(SQLITE_FCNTL_LAST_ERRNO);
+ DefInt(SQLITE_FCNTL_SIZE_HINT);
+ DefInt(SQLITE_FCNTL_CHUNK_SIZE);
+ DefInt(SQLITE_FCNTL_FILE_POINTER);
+ DefInt(SQLITE_FCNTL_SYNC_OMITTED);
+ DefInt(SQLITE_FCNTL_WIN32_AV_RETRY);
+ DefInt(SQLITE_FCNTL_PERSIST_WAL);
+ DefInt(SQLITE_FCNTL_OVERWRITE);
+ DefInt(SQLITE_FCNTL_VFSNAME);
+ DefInt(SQLITE_FCNTL_POWERSAFE_OVERWRITE);
+ DefInt(SQLITE_FCNTL_PRAGMA);
+ DefInt(SQLITE_FCNTL_BUSYHANDLER);
+ DefInt(SQLITE_FCNTL_TEMPFILENAME);
+ DefInt(SQLITE_FCNTL_MMAP_SIZE);
+ DefInt(SQLITE_FCNTL_TRACE);
+ DefInt(SQLITE_FCNTL_HAS_MOVED);
+ DefInt(SQLITE_FCNTL_SYNC);
+ DefInt(SQLITE_FCNTL_COMMIT_PHASETWO);
+ DefInt(SQLITE_FCNTL_WIN32_SET_HANDLE);
+ DefInt(SQLITE_FCNTL_WAL_BLOCK);
+ DefInt(SQLITE_FCNTL_ZIPVFS);
+ DefInt(SQLITE_FCNTL_RBU);
+ DefInt(SQLITE_FCNTL_VFS_POINTER);
+ DefInt(SQLITE_FCNTL_JOURNAL_POINTER);
+ DefInt(SQLITE_FCNTL_WIN32_GET_HANDLE);
+ DefInt(SQLITE_FCNTL_PDB);
+ DefInt(SQLITE_FCNTL_BEGIN_ATOMIC_WRITE);
+ DefInt(SQLITE_FCNTL_COMMIT_ATOMIC_WRITE);
+ DefInt(SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE);
+ DefInt(SQLITE_FCNTL_LOCK_TIMEOUT);
+ DefInt(SQLITE_FCNTL_DATA_VERSION);
+ DefInt(SQLITE_FCNTL_SIZE_LIMIT);
+ DefInt(SQLITE_FCNTL_CKPT_DONE);
+ DefInt(SQLITE_FCNTL_RESERVE_BYTES);
+ DefInt(SQLITE_FCNTL_CKPT_START);
+ DefInt(SQLITE_FCNTL_EXTERNAL_READER);
+ DefInt(SQLITE_FCNTL_CKSM_FILE);
+ } _DefGroup;
+
DefGroup(access){
DefInt(SQLITE_ACCESS_EXISTS);
DefInt(SQLITE_ACCESS_READWRITE);
<hr>
<div id='reverse-log-notice' class='hidden'>(Log output is in reverse order, newest first!)</div>
<div id='test-output'></div>
-
+ <!-- batch-runner.js "should" work with sqlite3-kvvfs so long as
+ its data sets don't violate the the storage limits. -->
+ <!--script src="sqlite3-kvvfs.js"></script-->
<script src="sqlite3.js"></script>
<script src="common/SqliteTestUtil.js"></script>
<script src="batch-runner.js"></script>
(function(){
const toss = function(...args){throw new Error(args.join(' '))};
const warn = console.warn.bind(console);
+ let sqlite3;
const App = {
e: {
btnReset: document.querySelector('#db-reset'),
cbReverseLog: document.querySelector('#cb-reverse-log-order')
},
+ db: Object.create(null),
cache:{},
metrics:{
/**
},
openDb: function(fn, unlinkFirst=true){
- if(this.db && this.db.ptr){
+ if(this.db.ptr){
toss("Already have an opened db.");
}
const capi = this.sqlite3.capi, wasm = capi.wasm;
const stack = wasm.scopedAllocPush();
let pDb = 0;
try{
- if(unlinkFirst && fn && ':memory:'!==fn){
- capi.wasm.sqlite3_wasm_vfs_unlink(fn);
+ if(unlinkFirst && fn){
+ if(':'!==fn[0]) capi.wasm.sqlite3_wasm_vfs_unlink(fn);
+ this.clearStorage();
}
const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
const ppDb = wasm.scopedAllocPtr();
}finally{
wasm.scopedAllocPop(stack);
}
- this.db = Object.create(null);
this.db.filename = fn;
this.db.ptr = pDb;
this.logHtml("Opened db:",fn);
},
closeDb: function(unlink=false){
- if(this.db && this.db.ptr){
+ if(this.db.ptr){
this.sqlite3.capi.sqlite3_close_v2(this.db.ptr);
this.logHtml("Closed db",this.db.filename);
- if(unlink) capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename);
+ if(unlink){
+ capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename);
+ this.clearStorage();
+ }
this.db.ptr = this.db.filename = undefined;
}
},
return p.catch((e)=>this.logErr("Error via evalFile("+fn+"):",e.message));
}/*evalFile()*/,
+ clearStorage: function(){
+ const sz = sqlite3.capi.sqlite3_web_kvvfs_size();
+ const n = sqlite3.capi.sqlite3_web_kvvfs_clear(this.db.filename || '');
+ this.logHtml("Cleared kvvfs local/sessionStorage:",
+ n,"entries totaling approximately",sz,"bytes.");
+ },
+
+ resetDb: function(){
+ if(this.db.ptr){
+ const fn = this.db.filename;
+ this.closeDb(true);
+ this.openDb(fn,false);
+ }
+ },
+
run: function(sqlite3){
delete this.run;
this.sqlite3 = sqlite3;
this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
this.logHtml("WASM heap size =",wasm.heap8().length);
this.loadSqlList();
- const pDir = capi.sqlite3_web_persistent_dir();
- const dbFile = pDir ? pDir+"/speedtest.db" : ":memory:";
- if(!pDir){
+ const pDir = 1 ? '' : capi.sqlite3_web_persistent_dir();
+ const dbFile = pDir ? pDir+"/speedtest.db" : (
+ sqlite3.capi.sqlite3_vfs_find('kvvfs') ? 'local' : ':memory:'
+ );
+ this.clearStorage();
+ if(pDir){
+ logHtml("Using persistent storage:",dbFile);
+ }else{
document.querySelector('#warn-opfs').remove();
}
this.openDb(dbFile, !!pDir);
who.evalFile(who.e.selSql.value);
}, false);
this.e.btnReset.addEventListener('click', function(){
- const fn = who.db.filename;
- if(fn){
- who.closeDb(true);
- who.openDb(fn,true);
- }
+ who.resetDb();
}, false);
this.e.btnExportMetrics.addEventListener('click', function(){
who.logHtml2('warning',"Triggering download of metrics CSV. Check your downloads folder.");
}
const timeTotal = performance.now() - timeStart;
who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))");
+ who.clearStorage();
}, false);
}/*run()*/
}/*App*/;
self.sqlite3TestModule.initSqlite3().then(function(theEmccModule){
self._MODULE = theEmccModule /* this is only to facilitate testing from the console */;
+ sqlite3 = theEmccModule.sqlite3;
App.run(theEmccModule.sqlite3);
});
})();
<button id='btn-clear-storage'>Clear storage</button>
<button id='btn-init-db'>(Re)init db</button>
<button id='btn-select1'>Select db rows</button>
+ <button id='btn-storage-size'>Approx. storage size</button>
</div>
</fieldset>
<div id='test-output'></div>
wasm = capi.wasm;
log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
T.assert( 0 !== capi.sqlite3_vfs_find(null) );
- if(!oo.DB.clearKvvfsStorage){
+ if(!capi.sqlite3_vfs_find('kvvfs')){
warn("This build is not kvvfs-capable.");
return;
}
- const dbStorage = 1 ? ':sessionStorage:' : ':localStorage:';
- const theStore = 's'===dbStorage[1] ? sessionStorage : localStorage;
+ const dbStorage = 1 ? 'session' : 'local';
+ const theStore = 's'===dbStorage[0] ? sessionStorage : localStorage;
/**
The names ':sessionStorage:' and ':localStorage:' are handled
via the DB class constructor, not the C level. In the C API,
the names "local" and "session" are the current (2022-09-12)
names for those keys, but that is subject to change.
*/
- const db = new oo.DB( dbStorage );
+ const db = new oo.DB( dbStorage, 'c', 'kvvfs' );
document.querySelector('#btn-clear-storage').addEventListener('click',function(){
- oo.DB.clearKvvfsStorage();
- log("kvvfs localStorage and sessionStorage cleared.");
+ const sz = capi.sqlite3_web_kvvfs_clear();
+ log("kvvfs localStorage and sessionStorage cleared:",sz,"entries.");
});
document.querySelector('#btn-clear-log').addEventListener('click',function(){
eOutput.innerText = '';
});
document.querySelector('#btn-init-db').addEventListener('click',function(){
- const saveSql = [];
try{
+ const saveSql = [];
db.exec({
- sql:["drop table if exists t;",
- "create table if not exists t(a);",
- "insert into t(a) values(?),(?),(?)"],
+ sql: ["drop table if exists t;",
+ "create table if not exists t(a);",
+ "insert into t(a) values(?),(?),(?)"],
bind: [performance.now() >> 0,
(performance.now() * 2) >> 0,
(performance.now() / 2) >> 0],
error(e.message);
}
});
+ document.querySelector('#btn-storage-size').addEventListener('click',function(){
+ log("sqlite3_web_kvvfs_size(",dbStorage,") says", capi.sqlite3_web_kvvfs_size(dbStorage),
+ "bytes");
+ });
log("Storage backend:",db.filename /* note that the name was internally translated */);
if(0===db.selectValue('select count(*) from sqlite_master')){
log("DB is empty. Use the init button to populate it.");
return v1>=(v2-factor) && v1<=(v2+factor);
};
+ let sqlite3;
+
const testBasicSanity = function(db,sqlite3){
const capi = sqlite3.capi;
log("Basic sanity tests...");
}
}/*testWasmUtil()*/;
+ const clearKvvfs = function(){
+ const sz = sqlite3.capi.sqlite3_web_kvvfs_size();
+ const n = sqlite3.capi.sqlite3_web_kvvfs_clear('');
+ log("Cleared kvvfs local/sessionStorage:",
+ n,"entries totaling approximately",sz,"bytes.");
+ };
+
const runTests = function(Module){
//log("Module",Module);
- const sqlite3 = Module.sqlite3,
- capi = sqlite3.capi,
+ sqlite3 = Module.sqlite3;
+ const capi = sqlite3.capi,
oo = sqlite3.oo1,
wasm = capi.wasm;
log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
let dbName = "/testing1.sqlite3";
let vfsName = undefined;
- if(oo.DB.clearKvvfsStorage){
+ if(capi.sqlite3_web_db_is_kvvfs()){
dbName = "local";
vfsName = 'kvvfs';
logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage",
"and selecting kvvfs-friendly db name:",dbName);
- oo.DB.clearKvvfsStorage();
+ clearKvvfs();
}
const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now();
+ log("capi.sqlite3_web_db_is_kvvfs() ==",capi.sqlite3_web_db_is_kvvfs(db.pointer));
try {
log("db.filename =",db.filename,"db.fileName() =",db.fileName());
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
});
}finally{
db.close();
+ if('kvvfs'===vfsName) clearKvvfs();
}
logHtml("Total Test count:",T.counter,"in",(performance.now() - startTime),"ms");
log('capi.wasm.exports',capi.wasm.exports);
-C Get\stesting1.js\sworking\swith\sa\skvvfs\sbuild.
-D 2022-09-12T22:27:00.037
+C Add/apply\svarious\skvvfs-specific\sutility\sAPIs\sto\sthe\sJS\slayer\sto\sassist\sin\stesting\sand\sanalysis.\sCorrect\sa\sbackwards\sdefault\sarg\scheck\sfor\ssqlite3ApiBootstrap().\sAdd\sexports\sfor\ssqlite3_db_handle(),\ssqlite3_file_control(),\sand\sthe\sSQLITE_FCNTL_xxx\senum.
+D 2022-09-13T19:27:03.599
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02
F ext/wasm/GNUmakefile b175a00599c976fe9e7bc02bbc1337b5e3b81d042320c3a0be1621d2c7a21b21
F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52
-F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1dfd067b3cbd9a49cb204097367cf2f8fe71b5a3b245d9d82a24779fd4ac2394
+F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
-F ext/wasm/api/sqlite3-api-cleanup.js 101919ec261644e2f6f0a59952fd9612127b69ea99b493277b2789ea478f9b6b
-F ext/wasm/api/sqlite3-api-glue.js 2bf536a38cde324cf352bc2c575f8e22c6d204d667c0eda5a254ba45318914bc
-F ext/wasm/api/sqlite3-api-oo1.js b498662748918c132aa59433ea2bd2ebb7e026549fd68506a1ae1ea94736f4f6
+F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
+F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
+F ext/wasm/api/sqlite3-api-oo1.js 4925f4736eb28fd3a6d4dbbd2c078c38be651aa5996041e7f0b81be36506608e
F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
-F ext/wasm/api/sqlite3-api-prologue.js 9e37ce4dfd74926d0df80dd7e72e33085db4bcee48e2c21236039be416a7dff2
+F ext/wasm/api/sqlite3-api-prologue.js c496adc0cf2ae427ee900aad01c09db97850bd8b6737f2849cab207c8415b839
F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
-F ext/wasm/api/sqlite3-wasm.c bf4637cf28463cada4b25f09651943c7ece004b253ef39b7ab68eaa60662aa09
-F ext/wasm/batch-runner.html 23209ade7981acce7ecd79d6eff9f4c5a4e8b14ae867ac27cd89b230be640fa6
-F ext/wasm/batch-runner.js 2abd146d3e3a66128ac0a2cc39bfd01e9811c9511fa10ec927d6649795f1ee50
+F ext/wasm/api/sqlite3-wasm.c 7d1760f3a864a9ae16cfb71543829d946a54384bd07af99a3adf9ef32913705f
+F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04
+F ext/wasm/batch-runner.js fb6a338aeef509f181f499700a8595bc578bbc54ef832e0cda7e7e7c10b90a18
F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962
F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19
F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c
-F ext/wasm/kvvfs1.html 83bac238d1e93ed102a461672fc58fe74e611e426708e9a5c66002c01eadb942
-F ext/wasm/kvvfs1.js 53721a42e0ec45f6978cc723e5de47f882517867d0fcff4c6ff05f4c422e27c4
+F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1
+F ext/wasm/kvvfs1.js a23ee98a78b9da379eab7a20d7ddf5283a684dd7a91685a84686c05216f96689
F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c
F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e
F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
-F ext/wasm/testing1.js 735120231d6e55b92964924fcb54a66484d8b2e30fe5a18c71081b3590344a5f
+F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8b1608a9a37131121cb3a0cb1e8dbad5e39dc1fc2a341d056f09537f179b2e7a
-R 77095a41dfb9b784dcab0078822e6eff
+P 333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79
+R fff963b52d5c3a76677d96f35303ab67
U stephan
-Z c624cb9efadffe8972e3df15b8dc9939
+Z e8c548c1774e1a39daa4183bad155966
# Remove this line to create a well-formed Fossil manifest.
-333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79
\ No newline at end of file
+0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2
\ No newline at end of file