mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-27 20:41:58 +03:00
Expand the worker1 'exec' op handling for per-row callbacks for API-level consistency and smooth some edges between worker1 core and worker1-promiser. Add worker1 'config-get' message to fetch the serializable parts of the sqlite3.config state. Improve the 'open' op's handling of the 'persistent' option (noting that we cannot yet test that case from a worker).
FossilOrigin-Name: 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
This commit is contained in:
@ -252,6 +252,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
TODO?: how can we define rowMode such that it uses
|
||||
rowMode of 'object' and returns a given named field from
|
||||
the object. Something like:
|
||||
|
||||
if(?what goes here?){
|
||||
out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]}
|
||||
.bind({obj:{}, colName: ???what goes here???}});
|
||||
break;
|
||||
}
|
||||
|
||||
Maybe rowMode:['colName1',... 'colNameN']? That could be
|
||||
ambiguous: might mean "return an object with just these
|
||||
columns".
|
||||
*/
|
||||
toss3("Invalid rowMode:",out.opt.rowMode);
|
||||
}
|
||||
}
|
||||
|
@ -42,25 +42,21 @@
|
||||
initialization is complete, as the initialization is synchronous.
|
||||
In some contexts, however, listening for the above message is
|
||||
a better fit.
|
||||
|
||||
Note that the worker-based interface can be slightly quirky because
|
||||
of its async nature. In particular, any number of messages may be posted
|
||||
to the worker before it starts handling any of them. If, e.g., an
|
||||
"open" operation fails, any subsequent messages will fail. The
|
||||
Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`)
|
||||
is more comfortable to use in that regard.
|
||||
|
||||
|
||||
TODO: hoist the message API docs from deep in this code to here.
|
||||
|
||||
*/
|
||||
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
sqlite3.initWorker1API = function(){
|
||||
'use strict';
|
||||
/**
|
||||
UNDER CONSTRUCTION
|
||||
|
||||
We need an API which can proxy the DB API via a Worker message
|
||||
interface. The primary quirky factor in such an API is that we
|
||||
cannot pass callback functions between the window thread and a
|
||||
worker thread, so we have to receive all db results via
|
||||
asynchronous message-passing. That requires an asychronous API
|
||||
with a distinctly different shape than OO API #1.
|
||||
|
||||
TODOs include, but are not necessarily limited to:
|
||||
|
||||
- Support for handling multiple DBs via this interface is under
|
||||
development.
|
||||
*/
|
||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||
if('function' !== typeof importScripts){
|
||||
toss("Cannot initalize the sqlite3 worker API in the main thread.");
|
||||
@ -86,12 +82,13 @@ sqlite3.initWorker1API = function(){
|
||||
};
|
||||
|
||||
/**
|
||||
Helper for managing Worker-level state.
|
||||
Internal helper for managing Worker-level state.
|
||||
*/
|
||||
const wState = {
|
||||
defaultDb: undefined,
|
||||
idSeq: 0,
|
||||
idMap: new WeakMap,
|
||||
xfer: [/*Temp holder for "transferable" postMessage() state.*/],
|
||||
open: function(opt){
|
||||
const db = new DB(opt.filename);
|
||||
this.dbs[getDbId(db)] = db;
|
||||
@ -109,9 +106,15 @@ sqlite3.initWorker1API = function(){
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
Posts the given worker message value. If xferList is provided,
|
||||
it must be an array, in which case a copy of it passed as
|
||||
postMessage()'s second argument and xferList.length is set to
|
||||
0.
|
||||
*/
|
||||
post: function(msg,xferList){
|
||||
if(xferList){
|
||||
self.postMessage( msg, xferList );
|
||||
if(xferList && xferList.length){
|
||||
self.postMessage( msg, Array.from(xferList) );
|
||||
xferList.length = 0;
|
||||
}else{
|
||||
self.postMessage(msg);
|
||||
@ -146,32 +149,40 @@ sqlite3.initWorker1API = function(){
|
||||
message type key. The onmessage() dispatcher attempts to
|
||||
dispatch all inbound messages to a method of this object,
|
||||
passing it the event.data part of the inbound event object. All
|
||||
methods must return a plain Object containing any response
|
||||
methods must return a plain Object containing any result
|
||||
state, which the dispatcher may amend. All methods must throw
|
||||
on error.
|
||||
*/
|
||||
const wMsgHandler = {
|
||||
xfer: [/*Temp holder for "transferable" postMessage() state.*/],
|
||||
/**
|
||||
Proxy for the DB constructor. Expects to be passed a single
|
||||
object or a falsy value to use defaults. The object may have a
|
||||
filename property to name the db file (see the DB constructor
|
||||
for peculiarities and transformations). The response is an
|
||||
object:
|
||||
object or a falsy value to use defaults:
|
||||
|
||||
{
|
||||
filename: db filename (possibly differing from the input),
|
||||
filename [=":memory:" or "" (unspecified)]: the db filename.
|
||||
See the sqlite3.oo1.DB constructor for peculiarities and transformations,
|
||||
|
||||
persistent [=false]: if true and filename is not one of ("",
|
||||
":memory:"), prepend
|
||||
sqlite3.capi.sqlite3_web_persistent_dir() to the given
|
||||
filename so that it is stored in persistent storage _if_ the
|
||||
environment supports it. If persistent storage is not
|
||||
supported, the filename is used as-is.
|
||||
}
|
||||
|
||||
The response object looks like:
|
||||
|
||||
{
|
||||
filename: db filename, possibly differing from the input.
|
||||
|
||||
dbId: an opaque ID value which must be passed in the message
|
||||
envelope to other calls in this API to tell them which
|
||||
db to use. If it is not provided to future calls, they
|
||||
will default to operating on the first-opened db.
|
||||
envelope to other calls in this API to tell them which db to
|
||||
use. If it is not provided to future calls, they will default
|
||||
to operating on the first-opened db.
|
||||
|
||||
persistent: prepend sqlite3.capi.sqlite3_web_persistent_dir()
|
||||
to the given filename so that it is stored
|
||||
in persistent storage _if_ the environment supports it.
|
||||
If persistent storage is not supported, the filename
|
||||
is used as-is.
|
||||
persistent: true if the given filename resides in the
|
||||
known-persistent storage, else false. This determination is
|
||||
independent of the `persistent` input argument.
|
||||
}
|
||||
*/
|
||||
open: function(ev){
|
||||
@ -179,28 +190,32 @@ sqlite3.initWorker1API = function(){
|
||||
if(args.simulateError){ // undocumented internal testing option
|
||||
toss("Throwing because of simulateError flag.");
|
||||
}
|
||||
if(args.persistent && args.filename){
|
||||
oargs.filename = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename;
|
||||
}else if('' === args.filename){
|
||||
oargs.filename = args.filename;
|
||||
const rc = Object.create(null);
|
||||
const pDir = sqlite3.capi.sqlite3_web_persistent_dir();
|
||||
if(!args.filename || ':memory:'===args.filename){
|
||||
oargs.filename = args.filename || '';
|
||||
}else if(pDir){
|
||||
oargs.filename = pDir + ('/'===args.filename[0] ? args.filename : ('/'+args.filename));
|
||||
}else{
|
||||
oargs.filename = args.filename || ':memory:';
|
||||
}
|
||||
oargs.filename = args.filename;
|
||||
}
|
||||
const db = wState.open(oargs);
|
||||
return {
|
||||
filename: db.filename,
|
||||
dbId: getDbId(db)
|
||||
};
|
||||
rc.filename = db.filename;
|
||||
rc.persistent = !!pDir && db.filename.startsWith(pDir);
|
||||
rc.dbId = getDbId(db);
|
||||
return rc;
|
||||
},
|
||||
/**
|
||||
Proxy for DB.close(). ev.args may be elided or an object with
|
||||
an `unlink` property. If that value is truthy then the db file
|
||||
(if the db is currently open) will be unlinked from the virtual
|
||||
filesystem, else it will be kept intact. The result object is:
|
||||
filesystem, else it will be kept intact, noting that unlink
|
||||
failure is ignored. The result object is:
|
||||
|
||||
{
|
||||
filename: db filename _if_ the db is opened when this
|
||||
is called, else the undefined value
|
||||
is called, else the undefined value
|
||||
|
||||
dbId: the ID of the closed b, or undefined if none is closed
|
||||
}
|
||||
|
||||
@ -211,7 +226,7 @@ sqlite3.initWorker1API = function(){
|
||||
const db = getMsgDb(ev,false);
|
||||
const response = {
|
||||
filename: db && db.filename,
|
||||
dbId: db ? getDbId(db) : undefined
|
||||
dbId: db && getDbId(db)
|
||||
};
|
||||
if(db){
|
||||
wState.close(db, ((ev.args && 'object'===typeof ev.args)
|
||||
@ -220,7 +235,7 @@ sqlite3.initWorker1API = function(){
|
||||
return response;
|
||||
},
|
||||
/**
|
||||
Proxy for DB.exec() which expects a single argument of type
|
||||
Proxy for oo1.DB.exec() which expects a single argument of type
|
||||
string (SQL to execute) or an options object in the form
|
||||
expected by exec(). The notable differences from exec()
|
||||
include:
|
||||
@ -234,12 +249,21 @@ sqlite3.initWorker1API = function(){
|
||||
message type key, in which case a callback function will be
|
||||
applied which posts each row result via:
|
||||
|
||||
postMessage({type: thatKeyType, row: theRow})
|
||||
postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow})
|
||||
|
||||
And, at the end of the result set (whether or not any
|
||||
result rows were produced), it will post an identical
|
||||
message with row:null to alert the caller than the result
|
||||
set is completed.
|
||||
And, at the end of the result set (whether or not any result
|
||||
rows were produced), it will post an identical message with
|
||||
(row=undefined, rowNumber=null) to alert the caller than the
|
||||
result set is completed. Note that a row value of `null` is
|
||||
a legal row result for certain `rowMode` values.
|
||||
|
||||
(Design note: we don't use (row=undefined, rowNumber=undefined)
|
||||
to indicate end-of-results because fetching those would be
|
||||
indistinguishable from fetching from an empty object unless the
|
||||
client used hasOwnProperty() (or similar) to distinguish
|
||||
"missing property" from "property with the undefined value".
|
||||
Similarly, `null` is a legal value for `row` in some case ,
|
||||
whereas the db layer won't emit a result value of `undefined`.)
|
||||
|
||||
The callback proxy must not recurse into this interface, or
|
||||
results are undefined. (It hypothetically cannot recurse
|
||||
@ -250,52 +274,73 @@ sqlite3.initWorker1API = function(){
|
||||
The response is the input options object (or a synthesized
|
||||
one if passed only a string), noting that
|
||||
options.resultRows and options.columnNames may be populated
|
||||
by the call to exec().
|
||||
|
||||
This opens/creates the Worker's db if needed.
|
||||
by the call to db.exec().
|
||||
*/
|
||||
exec: function(ev){
|
||||
const opt = (
|
||||
const rc = (
|
||||
'string'===typeof ev.args
|
||||
) ? {sql: ev.args} : (ev.args || Object.create(null));
|
||||
if(undefined===opt.rowMode){
|
||||
if(undefined===rc.rowMode){
|
||||
/* Since the default rowMode of 'stmt' is not useful
|
||||
for the Worker interface, we'll default to
|
||||
something else. */
|
||||
opt.rowMode = 'array';
|
||||
}else if('stmt'===opt.rowMode){
|
||||
toss("Invalid rowMode for exec(): stmt mode",
|
||||
rc.rowMode = 'array';
|
||||
}else if('stmt'===rc.rowMode){
|
||||
toss("Invalid rowMode for 'exec': stmt mode",
|
||||
"does not work in the Worker API.");
|
||||
}
|
||||
const db = getMsgDb(ev);
|
||||
if(opt.callback || Array.isArray(opt.resultRows)){
|
||||
if(rc.callback || Array.isArray(rc.resultRows)){
|
||||
// Part of a copy-avoidance optimization for blobs
|
||||
db._blobXfer = this.xfer;
|
||||
db._blobXfer = wState.xfer;
|
||||
}
|
||||
const callbackMsgType = opt.callback;
|
||||
const callbackMsgType = rc.callback;
|
||||
let rowNumber = 0;
|
||||
if('string' === typeof callbackMsgType){
|
||||
/* Treat this as a worker message type and post each
|
||||
row as a message of that type. */
|
||||
const that = this;
|
||||
opt.callback =
|
||||
(row)=>wState.post({type: callbackMsgType, row:row}, this.xfer);
|
||||
rc.callback =
|
||||
(row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer);
|
||||
}
|
||||
try {
|
||||
db.exec(opt);
|
||||
if(opt.callback instanceof Function){
|
||||
opt.callback = callbackMsgType;
|
||||
wState.post({type: callbackMsgType, row: null});
|
||||
db.exec(rc);
|
||||
if(rc.callback instanceof Function){
|
||||
rc.callback = callbackMsgType;
|
||||
wState.post({type: callbackMsgType, rowNumber: null, row: undefined});
|
||||
}
|
||||
}/*catch(e){
|
||||
console.warn("Worker is propagating:",e);throw e;
|
||||
}*/finally{
|
||||
delete db._blobXfer;
|
||||
if(opt.callback){
|
||||
opt.callback = callbackMsgType;
|
||||
}
|
||||
}
|
||||
return opt;
|
||||
}finally{
|
||||
delete db._blobXfer;
|
||||
if(rc.callback){
|
||||
rc.callback = callbackMsgType;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}/*exec()*/,
|
||||
/**
|
||||
Returns a JSON-friendly form of a _subset_ of sqlite3.config,
|
||||
sans any parts which cannot be serialized. Because we cannot,
|
||||
from here, distingush whether or not certain objects can be
|
||||
serialized, this routine selectively copies certain properties
|
||||
rather than trying JSON.stringify() and seeing what happens
|
||||
(the results are horrid if the config object contains an
|
||||
Emscripten module object).
|
||||
|
||||
In addition to the "real" config properties, it sythesizes
|
||||
the following:
|
||||
|
||||
- persistenceEnabled: true if persistent dir support is available,
|
||||
else false.
|
||||
*/
|
||||
'config-get': function(){
|
||||
const rc = Object.create(null), src = sqlite3.config;
|
||||
[
|
||||
'persistentDirName', 'bigIntEnabled'
|
||||
].forEach(function(k){
|
||||
if(Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k];
|
||||
});
|
||||
rc.persistenceEnabled = !!sqlite3.capi.sqlite3_web_persistent_dir();
|
||||
return rc;
|
||||
},
|
||||
/**
|
||||
TO(RE)DO, once we can abstract away access to the
|
||||
JS environment's virtual filesystem. Currently this
|
||||
@ -329,7 +374,7 @@ sqlite3.initWorker1API = function(){
|
||||
filename: db.filename,
|
||||
mimetype: 'application/x-sqlite3'
|
||||
};
|
||||
this.xfer.push(response.buffer.buffer);
|
||||
wState.xfer.push(response.buffer.buffer);
|
||||
return response;**/
|
||||
}/*export()*/,
|
||||
toss: function(ev){
|
||||
@ -344,22 +389,32 @@ sqlite3.initWorker1API = function(){
|
||||
form:
|
||||
|
||||
{ type: apiCommand,
|
||||
dbId: optional DB ID value (else uses a default db handle),
|
||||
args: apiArguments,
|
||||
dbId: optional DB ID value (else uses a default db handle),
|
||||
messageId: optional client-specific value
|
||||
}
|
||||
|
||||
As a rule, these commands respond with a postMessage() of their
|
||||
own in the form:
|
||||
|
||||
TODO: refactoring is underway.
|
||||
|
||||
The responses always have an object-format `result` part. If the
|
||||
inbound object has a `messageId` property, that property is
|
||||
own. The responses always have a `type` property equal to the
|
||||
input message's type and an object-format `result` part. If
|
||||
the inbound object has a `messageId` property, that property is
|
||||
always mirrored in the result object, for use in client-side
|
||||
dispatching of these asynchronous results. Exceptions thrown
|
||||
during processing result in an `error`-type event with a payload
|
||||
in the form:
|
||||
dispatching of these asynchronous results. For example:
|
||||
|
||||
{
|
||||
type: 'open',
|
||||
messageId: ...copied from inbound message...,
|
||||
dbId: ID of db which was opened,
|
||||
result: {
|
||||
dbId: repeat of ^^^, for API consistency's sake,
|
||||
filename: ...,
|
||||
persistent: false
|
||||
},
|
||||
...possibly other framework-internal/testing/debugging info...
|
||||
}
|
||||
|
||||
Exceptions thrown during processing result in an `error`-type
|
||||
event with a payload in the form:
|
||||
|
||||
{ type: 'error',
|
||||
dbId: DB handle ID,
|
||||
@ -413,10 +468,15 @@ sqlite3.initWorker1API = function(){
|
||||
workerReceivedTime: arrivalTime,
|
||||
workerRespondTime: performance.now(),
|
||||
departureTime: ev.departureTime,
|
||||
// TODO: move the timing bits into...
|
||||
//timing:{
|
||||
// departure: ev.departureTime,
|
||||
// workerReceived: arrivalTime,
|
||||
// workerResponse: performance.now();
|
||||
//},
|
||||
result: result
|
||||
}, wMsgHandler.xfer);
|
||||
}, wState.xfer);
|
||||
};
|
||||
self.postMessage({type:'sqlite3-api',result:'worker1-ready'});
|
||||
}.bind({self, sqlite3});
|
||||
});
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
manipulated via a Promise-based interface and returns a factory
|
||||
function which returns Promises for communicating with the worker.
|
||||
This proxy has an _almost_ identical interface to the normal
|
||||
worker API, with any exceptions noted below.
|
||||
worker API, with any exceptions documented below.
|
||||
|
||||
It requires a configuration object with the following properties:
|
||||
|
||||
@ -127,26 +127,34 @@
|
||||
- exec's {callback: STRING} option does not work via this
|
||||
interface (it triggers an exception), but {callback: function}
|
||||
does and works exactly like the STRING form does in the Worker:
|
||||
the callback is called one time for each row of the result set
|
||||
and once more, at the end, passed only `null`, to indicate that
|
||||
the callback is called one time for each row of the result set,
|
||||
passed the same worker message format as the worker API emits:
|
||||
|
||||
{type:typeString, row:VALUE, rowNumber:1-based-#}
|
||||
|
||||
Where `typeString` is an internally-synthesized message type string
|
||||
used temporarily for worker message dispatching. It can be ignored
|
||||
by all client code except that which tests this API. The `row`
|
||||
property contains the row result in the form implied by the
|
||||
`rowMode` option (defaulting to `'array'`). The `rowNumber` is a
|
||||
1-based integer value incremented by 1 on each call into th
|
||||
callback.
|
||||
|
||||
At the end of the result set, the same event is fired with
|
||||
(row=undefined, rowNumber=null) to indicate that
|
||||
the end of the result set has been reached. Note that the rows
|
||||
arrive via worker-posted messages, with all the implications
|
||||
of that.
|
||||
|
||||
|
||||
TODO?: a config option which causes it to queue up events to fire
|
||||
one at a time and flush the event queue on the first error. The
|
||||
main use for this is test runs which must fail at the first error.
|
||||
*/
|
||||
self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||
// Inspired by: https://stackoverflow.com/a/52439530
|
||||
let idNumber = 0;
|
||||
const handlerMap = Object.create(null);
|
||||
const noop = function(){};
|
||||
const err = config.onerror || noop;
|
||||
const debug = config.debug || noop;
|
||||
const idTypeMap = config.generateMessageId ? undefined : Object.create(null);
|
||||
const genMsgId = config.generateMessageId || function(msg){
|
||||
return msg.type+'#'+(++idNumber);
|
||||
return msg.type+'#'+(idTypeMap[msg.type] = (idTypeMap[msg.type]||0) + 1);
|
||||
};
|
||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||
if('function'===typeof config.worker) config.worker = config.worker();
|
||||
@ -162,7 +170,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||
}
|
||||
msgHandler = handlerMap[ev.type] /* check for exec per-row callback */;
|
||||
if(msgHandler && msgHandler.onrow){
|
||||
msgHandler.onrow(ev.row);
|
||||
msgHandler.onrow(ev);
|
||||
return;
|
||||
}
|
||||
if(config.onunhandled) config.onunhandled(arguments[0]);
|
||||
@ -183,13 +191,10 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||
default:
|
||||
break;
|
||||
}
|
||||
try {
|
||||
msgHandler.resolve(ev);
|
||||
}catch(e){
|
||||
msgHandler.reject(e);
|
||||
}
|
||||
try {msgHandler.resolve(ev)}
|
||||
catch(e){msgHandler.reject(e)}
|
||||
}/*worker.onmessage()*/;
|
||||
return function(/*(msgType, msgArgs) || (msg)*/){
|
||||
return function(/*(msgType, msgArgs) || (msgEnvelope)*/){
|
||||
let msg;
|
||||
if(1===arguments.length){
|
||||
msg = arguments[0];
|
||||
@ -206,15 +211,29 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||
msg.departureTime = performance.now();
|
||||
const proxy = Object.create(null);
|
||||
proxy.message = msg;
|
||||
let cbId /* message handler ID for exec on-row callback proxy */;
|
||||
let rowCallbackId /* message handler ID for exec on-row callback proxy */;
|
||||
if('exec'===msg.type && msg.args){
|
||||
if('function'===typeof msg.args.callback){
|
||||
cbId = genMsgId(msg)+':row';
|
||||
rowCallbackId = msg.messageId+':row';
|
||||
proxy.onrow = msg.args.callback;
|
||||
msg.args.callback = cbId;
|
||||
handlerMap[cbId] = proxy;
|
||||
msg.args.callback = rowCallbackId;
|
||||
handlerMap[rowCallbackId] = proxy;
|
||||
}else if('string' === typeof msg.args.callback){
|
||||
toss("exec callback may not be a string when using the Promise interface.");
|
||||
/**
|
||||
Design note: the reason for this limitation is that this
|
||||
API takes over worker.onmessage() and the client has no way
|
||||
of adding their own message-type handlers to it. Per-row
|
||||
callbacks are implemented as short-lived message.type
|
||||
mappings for worker.onmessage().
|
||||
|
||||
We "could" work around this by providing a new
|
||||
config.fallbackMessageHandler (or some such) which contains
|
||||
a map of event type names to callbacks. Seems like overkill
|
||||
for now, seeing as the client can pass callback functions
|
||||
to this interface (whereas the string-form "callback" is
|
||||
needed for the over-the-Worker interface).
|
||||
*/
|
||||
}
|
||||
}
|
||||
//debug("requestWork", msg);
|
||||
@ -225,7 +244,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||
debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg);
|
||||
config.worker.postMessage(msg);
|
||||
});
|
||||
if(cbId) p = p.finally(()=>delete handlerMap[cbId]);
|
||||
if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]);
|
||||
return p;
|
||||
};
|
||||
}/*sqlite3Worker1Promiser()*/;
|
||||
|
@ -60,24 +60,41 @@
|
||||
delete self.sqlite3Worker1Promiser;
|
||||
|
||||
const wtest = async function(msgType, msgArgs, callback){
|
||||
if(2===arguments.length && 'function'===typeof msgArgs){
|
||||
callback = msgArgs;
|
||||
msgArgs = undefined;
|
||||
}
|
||||
const p = workerPromise({type: msgType, args:msgArgs});
|
||||
return callback ? p.then(callback).finally(testCount) : p;
|
||||
};
|
||||
|
||||
const runTests = async function(){
|
||||
const dbFilename = '/testing2.sqlite3';
|
||||
logHtml('',
|
||||
"Sending 'open' message and waiting for its response before continuing.");
|
||||
startTime = performance.now();
|
||||
|
||||
let sqConfig;
|
||||
await wtest('config-get', (ev)=>{
|
||||
const r = ev.result;
|
||||
log('sqlite3.config subset:', r);
|
||||
T.assert('boolean' === typeof r.bigIntEnabled)
|
||||
.assert('string'===typeof r.persistentDirName)
|
||||
.assert('boolean' === typeof r.persistenceEnabled);
|
||||
sqConfig = r;
|
||||
});
|
||||
logHtml('',
|
||||
"Sending 'open' message and waiting for its response before continuing...");
|
||||
|
||||
await wtest('open', {
|
||||
filename: dbFilename,
|
||||
persistent: true,
|
||||
persistent: sqConfig.persistenceEnabled,
|
||||
simulateError: 0 /* if true, fail the 'open' */,
|
||||
}, function(ev){
|
||||
log("then open result",ev);
|
||||
T.assert(1 && (dbFilename===ev.result.filename
|
||||
|| (sqlite3TestModule.sqlite3ApiConfig.persistentDirName
|
||||
+ dbFilename)==ev.result.filename))
|
||||
const r = ev.result;
|
||||
log("then open result",r);
|
||||
T.assert(r.persistent === sqConfig.persistenceEnabled)
|
||||
.assert(r.persistent
|
||||
? (dbFilename!==r.filename)
|
||||
: (dbFilename==r.filename))
|
||||
.assert(ev.dbId)
|
||||
.assert(ev.messageId)
|
||||
.assert(promiserConfig.dbId === ev.dbId);
|
||||
@ -145,11 +162,17 @@
|
||||
.assert(3 === ev.resultRows[1][0]);
|
||||
});
|
||||
|
||||
const resultRowTest1 = function f(row){
|
||||
const resultRowTest1 = function f(ev){
|
||||
if(undefined === f.counter) f.counter = 0;
|
||||
if(row) ++f.counter;
|
||||
//log("exec() result row:",row);
|
||||
T.assert(null===row || 'number' === typeof row.b);
|
||||
if(null === ev.rowNumber){
|
||||
/* End of result set. */
|
||||
T.assert(undefined === ev.row);
|
||||
}else{
|
||||
T.assert(ev.rowNumber > 0);
|
||||
++f.counter;
|
||||
}
|
||||
log("exec() result row:",ev);
|
||||
T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
|
||||
};
|
||||
await wtest('exec',{
|
||||
sql: 'select a a, b b from t order by a',
|
||||
@ -195,10 +218,9 @@
|
||||
T.assert('string' === typeof ev.result.filename);
|
||||
});
|
||||
|
||||
await wtest('close').then((ev)=>{
|
||||
await wtest('close', (ev)=>{
|
||||
T.assert(undefined === ev.result.filename);
|
||||
log("That's all, folks!");
|
||||
});
|
||||
}).finally(()=>log("That's all, folks!"));
|
||||
}/*runTests2()*/;
|
||||
|
||||
|
||||
|
@ -115,9 +115,15 @@
|
||||
},
|
||||
resultRowTest1: function f(ev){
|
||||
if(undefined === f.counter) f.counter = 0;
|
||||
if(ev.row) ++f.counter;
|
||||
//log("exec() result row:",ev.row);
|
||||
T.assert(null===ev.row || 'number' === typeof ev.row.b);
|
||||
if(null === ev.rowNumber){
|
||||
/* End of result set. */
|
||||
T.assert(undefined === ev.row);
|
||||
}else{
|
||||
T.assert(ev.rowNumber > 0);
|
||||
++f.counter;
|
||||
}
|
||||
//log("exec() result row:",ev);
|
||||
T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
|
||||
}
|
||||
};
|
||||
|
||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C js:\sresolve\sthe\smysterious\s"extra"\sunhandled\sexception\snotification,\scaused\sby\sinadvertently\sforking\sone\spromise\sinto\stwo\sseparate\sones\s(failing\sto\sproperly\sreassign\sa\sthen()\sresult).\sFix\sa\stypo\sin\snew\sWorker\s1\scode\swhich\scaused\sthe\sDB(filename)\sname\sto\sbe\sincorrect.
|
||||
D 2022-08-24T14:50:10.920
|
||||
C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker).
|
||||
D 2022-08-24T18:39:46.246
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
|
||||
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de
|
||||
F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3
|
||||
F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc
|
||||
F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a
|
||||
F ext/wasm/api/sqlite3-api-worker1.js b23f66ef5afd350a17fbadb795007098e518a40e5c7c439cd83ef34aa55a45af
|
||||
F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
|
||||
F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
|
||||
@ -508,14 +508,14 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43
|
||||
F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
|
||||
F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9
|
||||
F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0
|
||||
F ext/wasm/sqlite3-worker1-promiser.js 9638b0ced7f02806c3220b616f08729dde9eb13fb56e125cd4759f40bfa81210
|
||||
F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
|
||||
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
||||
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
|
||||
F ext/wasm/testing-worker1-promiser.js 931d909c769c57292f1cafdf10c7dab402d17cd16a6d0ec32089f67b559b058f
|
||||
F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d
|
||||
F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
|
||||
F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
|
||||
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
|
||||
F ext/wasm/testing2.js f01e8d7b32f6d4790f8352bf8dc17d2b860afa58f6c8ea1f824ef1c0d2068138
|
||||
F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878
|
||||
R a11ec2b23afafa9cff8838dfde907f0c
|
||||
P 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
|
||||
R 4d53a1266e528a954f73a792c3648256
|
||||
U stephan
|
||||
Z 64f88270da37ddd3ce54e6e07b8ab1af
|
||||
Z cffa6daad399e2d401f0fd69f4f04b4f
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
|
||||
509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
|
Reference in New Issue
Block a user