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

wasm binding: consolidated the two sqlite3_prepare_v2() bindings behind a single dispathcer. Various internal cleanups and refactoring. Branched because trunk is in pencils-down mode for pending 3.39 release.

FossilOrigin-Name: ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c
This commit is contained in:
stephan
2022-06-25 02:37:57 +00:00
parent 86bc5e41bb
commit d45ac3947d
4 changed files with 202 additions and 66 deletions

View File

@ -166,9 +166,29 @@ Module.postRun.push(function(namespace/*the module object, the target for
SQLITE_INNOCUOUS: 0x000200000, SQLITE_INNOCUOUS: 0x000200000,
/* sqlite encodings, used for creating UDFs, noting that we /* sqlite encodings, used for creating UDFs, noting that we
will only support UTF8. */ will only support UTF8. */
SQLITE_UTF8: 1 SQLITE_UTF8: 1,
/* Values for the final argument of sqlite3_result_blob(),
noting that these are interpreted in WASM as pointer
values. */
SQLITE_TRANSIENT: -1,
SQLITE_STATIC: 0,
/**
Holds state which are specific to the WASM-related
infrastructure and glue code. It is not expected that client
code will normally need these, but they're exposed here in case it
does.
*/
wasm: {
/**
Proxy for SQM.allocate(). TODO: remove deprecated allocate(),
use _malloc(). The kicker is that allocate() uses
module-init-internal state which isn't normally visible to
us.
*/
allocate: (slab, allocator=SQM.ALLOC_NORMAL)=>SQM.allocate(slab, allocator)
}
}; };
const cwrap = SQM.cwrap;
[/* C-side functions to bind. Each entry is an array with 3 or 4 [/* C-side functions to bind. Each entry is an array with 3 or 4
elements: elements:
@ -219,14 +239,8 @@ Module.postRun.push(function(namespace/*the module object, the target for
["sqlite3_open", "number", ["string", "number"]], ["sqlite3_open", "number", ["string", "number"]],
//["sqlite3_open_v2", "number", ["string", "number", "number", "string"]], //["sqlite3_open_v2", "number", ["string", "number", "number", "string"]],
//^^^^ TODO: add the flags needed for the 3rd arg //^^^^ TODO: add the flags needed for the 3rd arg
["sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"]], /* sqlite3_prepare_v2() is handled separately due to us requiring two
["sqlite3_prepare_v2_sqlptr", "sqlite3_prepare_v2", different sets of semantics for that function. */
/* Impl which requires that the 2nd argument be a pointer to
the SQL string, instead of being converted to a
string. This is used for cases where we require a non-NULL
value for the final argument (exec()'ing multiple
statements from one input string). */
"number", ["number", "number", "number", "number", "number"]],
["sqlite3_reset", "number", ["number"]], ["sqlite3_reset", "number", ["number"]],
["sqlite3_result_blob",null,["number", "number", "number", "number"]], ["sqlite3_result_blob",null,["number", "number", "number", "number"]],
["sqlite3_result_double",null,["number", "number"]], ["sqlite3_result_double",null,["number", "number"]],
@ -245,7 +259,104 @@ Module.postRun.push(function(namespace/*the module object, the target for
//["sqlite3_normalized_sql", "string", ["number"]] //["sqlite3_normalized_sql", "string", ["number"]]
].forEach(function(a){ ].forEach(function(a){
const k = (4==a.length) ? a.shift() : a[0]; const k = (4==a.length) ? a.shift() : a[0];
api[k] = cwrap.apply(this, a); api[k] = SQM.cwrap.apply(this, a);
});
/**
Proxies for variants of sqlite3_prepare_v2() which have
differing JS/WASM binding semantics.
*/
const prepareMethods = {
/**
This binding expects a JS string as its 2nd argument and
null as its final argument. In order to compile multiple
statements from a single string, the "full" impl (see
below) must be used.
*/
basic: SQM.cwrap('sqlite3_prepare_v2',
"number", ["number", "string", "number"/*MUST always be negative*/,
"number", "number"/*MUST be 0 or null or undefined!*/]),
/* Impl which requires that the 2nd argument be a pointer to
the SQL string, instead of being converted to a
string. This variant is necessary for cases where we
require a non-NULL value for the final argument
(exec()'ing multiple statements from one input
string). For simpler cases, where only the first statement
in the SQL string is required, the wrapper named
sqlite3_prepare_v2() is sufficient and easier to use
because it doesn't require dealing with pointers.
TODO: hide both of these methods behind a single hand-written
sqlite3_prepare_v2() wrapper which dispatches to the appropriate impl.
*/
full: SQM.cwrap('sqlite3_prepare_v2',
"number", ["number", "number", "number"/*MUST always be negative*/,
"number", "number"]),
};
const uint8ToString = (str)=>new TextDecoder('utf-8').decode(str);
//const stringToUint8 = (sql)=>new TextEncoder('utf-8').encode(sql);
/**
sqlite3_prepare_v2() binding which handles two different uses
with differing JS/WASM semantics:
1) sqlite3_prepare_v2(pDb, sqlString, -1, ppStmt [, null])
2) sqlite3_prepare_v2(pDb, sqlPointer, -1, ppStmt, sqlPointerToPointer)
Note that the SQL length argument (the 3rd argument) must
always be negative because it must be a byte length and that
value is expensive to calculate from JS (where we get the
character length of strings). It is retained in this API's
interface for code/documentation compatibility reasons but is
currently _always_ ignored. When using the 2nd form of this
call, it is critical that the custom-allocated string be
terminated with a 0 byte. (Potential TODO: if this value is >0,
assume the caller knows precisely what they're doing and pass
it on as-is. That approach currently seems fraught with peril.)
In usage (1), the 2nd argument must be of type string or
Uint8Array (which is assumed to hold SQL). If it is, this
function assumes case (1) and calls the underling C function
with:
(pDb, sql, -1, ppStmt, null)
If sql is not a string or Uint8Array, it must be a _pointer_ to
a string which was allocated via api.wasm.allocateUTF8OnStack()
or equivalent (TODO: define "or equivalent"). In that case, the
final argument may be 0/null/undefined or must be a pointer to
which the "tail" of the compiled SQL is written, as documented
for the C-side sqlite3_prepare_v2(). In case (2), the
underlying C function is called with:
(pDb, sql, -1, ppStmt, pzTail)
It returns its result and compiled statement as documented in
the C API. Fetching the output pointer (4th argument) requires using
api.wasm.getValue().
*/
api.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){
if(sql instanceof Uint8Array) sql = uint8ToString(sql);
/* ^^^ TODO: confirm whether this conversion is really
necessary or whether passing on the array as-is will work
as if it were a string. */
switch(typeof sql){
case 'string': return prepareMethods.basic(pDb, sql, -1, ppStmt, null);
case 'number': return prepareMethods.full(pDb, sql, -1, ppStmt, pzTail);
default: toss("Invalid SQL argument type for sqlite3_prepare_v2().");
}
};
/** Populate api.wasm... */
['getValue','setValue', 'stackSave', 'stackRestore', 'stackAlloc',
'allocateUTF8OnStack', '_malloc', '_free',
'addFunction', 'removeFunction'
].forEach(function(m){
if(undefined === (api.wasm[m] = SQM[m])){
toss("Internal init error: Module."+m+" not found.");
}
}); });
/* What follows is colloquially known as "OO API #1". It is a /* What follows is colloquially known as "OO API #1". It is a
@ -255,8 +366,6 @@ Module.postRun.push(function(namespace/*the module object, the target for
the sqlite3 binding if, e.g., the wrapper is in the main thread the sqlite3 binding if, e.g., the wrapper is in the main thread
and the sqlite3 API is in a worker. */ and the sqlite3 API is in a worker. */
/** Memory for use in some pointer-to-pointer-passing routines. */
const pPtrArg = stackAlloc(4);
/** Throws a new error, concatenating all args with a space between /** Throws a new error, concatenating all args with a space between
each. */ each. */
const toss = function(){ const toss = function(){
@ -322,9 +431,12 @@ Module.postRun.push(function(namespace/*the module object, the target for
} }
FS.createDataFile("/", fn, buffer, true, true); FS.createDataFile("/", fn, buffer, true, true);
} }
setValue(pPtrArg, 0, "i32"); const stack = api.wasm.stackSave();
this.checkRc(api.sqlite3_open(fn, pPtrArg)); const ppDb = api.wasm.stackAlloc(4) /* output (sqlite3**) arg */;
this._pDb = getValue(pPtrArg, "i32"); api.wasm.setValue(ppDb, 0, "i32");
try {this.checkRc(api.sqlite3_open(fn, ppDb));}
finally{api.wasm.stackRestore(stack);}
this._pDb = api.wasm.getValue(ppDb, "i32");
this.filename = fn; this.filename = fn;
this._statements = {/*map of open Stmt _pointers_ to Stmt*/}; this._statements = {/*map of open Stmt _pointers_ to Stmt*/};
this._udfs = {/*map of UDF names to wasm function _pointers_*/}; this._udfs = {/*map of UDF names to wasm function _pointers_*/};
@ -378,7 +490,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
DB.execMulti(). Does the argument processing/validation, throws DB.execMulti(). Does the argument processing/validation, throws
on error, and returns a new object on success: on error, and returns a new object on success:
{ sql: the SQL, obt: optionsObj, cbArg: function} { sql: the SQL, opt: optionsObj, cbArg: function}
cbArg is only set if the opt.callback is set, in which case cbArg is only set if the opt.callback is set, in which case
it's a function which expects to be passed the current Stmt it's a function which expects to be passed the current Stmt
@ -386,12 +498,13 @@ Module.postRun.push(function(namespace/*the module object, the target for
the input arguments. the input arguments.
*/ */
const parseExecArgs = function(args){ const parseExecArgs = function(args){
const out = {}; const out = {opt:{}};
switch(args.length){ switch(args.length){
case 1: case 1:
if('string'===typeof args[0]){ if('string'===typeof args[0]){
out.sql = args[0]; out.sql = args[0];
out.opt = {}; }else if(args[0] instanceof Uint8Array){
out.sql = args[0];
}else if(args[0] && 'object'===typeof args[0]){ }else if(args[0] && 'object'===typeof args[0]){
out.opt = args[0]; out.opt = args[0];
out.sql = out.opt.sql; out.sql = out.opt.sql;
@ -403,7 +516,11 @@ Module.postRun.push(function(namespace/*the module object, the target for
break; break;
default: toss("Invalid argument count for exec()."); default: toss("Invalid argument count for exec().");
}; };
if('string'!==typeof out.sql) toss("Missing SQL argument."); if(out.sql instanceof Uint8Array){
out.sql = uint8ToString(out.sql);
}else if('string'!==typeof out.sql){
toss("Missing SQL argument.");
}
if(out.opt.callback || out.opt.resultRows){ if(out.opt.callback || out.opt.resultRows){
switch((undefined===out.opt.rowMode) switch((undefined===out.opt.rowMode)
? 'stmt' : out.opt.rowMode) { ? 'stmt' : out.opt.rowMode) {
@ -453,7 +570,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
delete that._statements[k]; delete that._statements[k];
if(s && s._pStmt) s.finalize(); if(s && s._pStmt) s.finalize();
}); });
Object.values(this._udfs).forEach(SQM.removeFunction); Object.values(this._udfs).forEach(api.wasm.removeFunction);
delete this._udfs; delete this._udfs;
delete this._statements; delete this._statements;
api.sqlite3_close_v2(this._pDb); api.sqlite3_close_v2(this._pDb);
@ -481,13 +598,21 @@ Module.postRun.push(function(namespace/*the module object, the target for
/** /**
Compiles the given SQL and returns a prepared Stmt. This is Compiles the given SQL and returns a prepared Stmt. This is
the only way to create new Stmt objects. Throws on error. the only way to create new Stmt objects. Throws on error.
The given SQL must be a string, a Uint8Array holding SQL,
or a WASM pointer to memory allocated using
api.wasm.allocateUTF8OnStack() (or equivalent (a term which
is yet to be defined precisely)).
*/ */
prepare: function(sql){ prepare: function(sql){
affirmDbOpen(this); affirmDbOpen(this);
setValue(pPtrArg,0,"i32"); const stack = api.wasm.stackSave();
this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null)); const ppStmt = api.wasm.stackAlloc(4)/* output (sqlite3_stmt**) arg */;
const pStmt = getValue(pPtrArg, "i32"); api.wasm.setValue(ppStmt, 0, "i32");
if(!pStmt) toss("Empty SQL is not permitted."); try {this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, ppStmt, null));}
finally {api.wasm.stackRestore(stack);}
const pStmt = api.wasm.getValue(ppStmt, "i32");
if(!pStmt) toss("Cannot prepare empty SQL.");
const stmt = new Stmt(this, pStmt, BindTypes); const stmt = new Stmt(this, pStmt, BindTypes);
this._statements[pStmt] = stmt; this._statements[pStmt] = stmt;
return stmt; return stmt;
@ -585,7 +710,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
properties: properties:
- .sql = the SQL to run (unless it's provided as the first - .sql = the SQL to run (unless it's provided as the first
argument). argument). This must be of type string or Uint8Array.
- .bind = a single value valid as an argument for - .bind = a single value valid as an argument for
Stmt.bind(). This is ONLY applied to the FIRST non-empty Stmt.bind(). This is ONLY applied to the FIRST non-empty
@ -638,23 +763,27 @@ Module.postRun.push(function(namespace/*the module object, the target for
? arguments[0] : parseExecArgs(arguments)); ? arguments[0] : parseExecArgs(arguments));
if(!arg.sql) return this; if(!arg.sql) return this;
const opt = arg.opt; const opt = arg.opt;
const stack = stackSave(); const stack = api.wasm.stackSave();
let stmt; let stmt;
let bind = opt.bind; let bind = opt.bind;
let rowMode = ( let rowMode = (
(opt.callback && opt.rowMode) (opt.callback && opt.rowMode)
? opt.rowMode : false); ? opt.rowMode : false);
try{ try{
let pSql = SQM.allocateUTF8OnStack(arg.sql) const sql = (arg.sql instanceof Uint8Array)
const pzTail = stackAlloc(4); ? uint8ToString(arg.sql)
while(getValue(pSql, "i8")){ : arg.sql;
setValue(pPtrArg, 0, "i32"); let pSql = api.wasm.allocateUTF8OnStack(sql)
setValue(pzTail, 0, "i32"); const ppStmt = api.wasm.stackAlloc(8) /* output (sqlite3_stmt**) arg */;
this.checkRc(api.sqlite3_prepare_v2_sqlptr( const pzTail = ppStmt + 4 /* final arg to sqlite3_prepare_v2_sqlptr() */;
this._pDb, pSql, -1, pPtrArg, pzTail while(api.wasm.getValue(pSql, "i8")){
api.wasm.setValue(ppStmt, 0, "i32");
api.wasm.setValue(pzTail, 0, "i32");
this.checkRc(api.sqlite3_prepare_v2(
this._pDb, pSql, -1, ppStmt, pzTail
)); ));
const pStmt = getValue(pPtrArg, "i32"); const pStmt = api.wasm.getValue(ppStmt, "i32");
pSql = getValue(pzTail, "i32"); pSql = api.wasm.getValue(pzTail, "i32");
if(!pStmt) continue; if(!pStmt) continue;
if(opt.saveSql){ if(opt.saveSql){
opt.saveSql.push(api.sqlite3_sql(pStmt).trim()); opt.saveSql.push(api.sqlite3_sql(pStmt).trim());
@ -683,7 +812,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
delete stmt._isLocked; delete stmt._isLocked;
stmt.finalize(); stmt.finalize();
} }
stackRestore(stack); api.wasm.stackRestore(stack);
} }
return this; return this;
}/*execMulti()*/, }/*execMulti()*/,
@ -736,7 +865,6 @@ Module.postRun.push(function(namespace/*the module object, the target for
- .directOnly = SQLITE_DIRECTONLY - .directOnly = SQLITE_DIRECTONLY
- .innocuous = SQLITE_INNOCUOUS - .innocuous = SQLITE_INNOCUOUS
Maintenance reminder: the ability to add new Maintenance reminder: the ability to add new
WASM-accessible functions to the runtime requires that the WASM-accessible functions to the runtime requires that the
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
@ -769,7 +897,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
let i, pVal, valType, arg; let i, pVal, valType, arg;
const tgt = []; const tgt = [];
for(i = 0; i < argc; ++i){ for(i = 0; i < argc; ++i){
pVal = getValue(pArgv + (4 * i), "i32"); pVal = api.wasm.getValue(pArgv + (4 * i), "i32");
valType = api.sqlite3_value_type(pVal); valType = api.sqlite3_value_type(pVal);
switch(valType){ switch(valType){
case api.SQLITE_INTEGER: case api.SQLITE_INTEGER:
@ -806,8 +934,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
break; break;
} }
case 'string': case 'string':
api.sqlite3_result_text(pCx, val, -1, api.sqlite3_result_text(pCx, val, -1, api.SQLITE_TRANSIENT);
-1/*==SQLITE_TRANSIENT*/);
break; break;
case 'object': case 'object':
if(null===val) { if(null===val) {
@ -815,9 +942,10 @@ Module.postRun.push(function(namespace/*the module object, the target for
break; break;
}else if(undefined!==val.length){ }else if(undefined!==val.length){
const pBlob = const pBlob =
SQM.allocate(val, SQM.ALLOC_NORMAL); api.wasm.allocate(val);
api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/); api.sqlite3_result_blob(pCx, pBlob, val.length,
SQM._free(blobptr); api.SQLITE_TRANSIENT);
api.wasm._free(blobptr);
break; break;
} }
// else fall through // else fall through
@ -833,7 +961,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
api.sqlite3_result_error(pCx, e.message, -1); api.sqlite3_result_error(pCx, e.message, -1);
} }
}; };
const pUdf = SQM.addFunction(wrapper, "viii"); const pUdf = api.wasm.addFunction(wrapper, "viii");
let fFlags = 0; let fFlags = 0;
if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC;
if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY; if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY;
@ -846,11 +974,11 @@ Module.postRun.push(function(namespace/*the module object, the target for
api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
}catch(e){ }catch(e){
SQM.removeFunction(pUdf); api.wasm.removeFunction(pUdf);
throw e; throw e;
} }
if(this._udfs.hasOwnProperty(name)){ if(this._udfs.hasOwnProperty(name)){
SQM.removeFunction(this._udfs[name]); api.wasm.removeFunction(this._udfs[name]);
} }
this._udfs[name] = pUdf; this._udfs[name] = pUdf;
return this; return this;
@ -999,7 +1127,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
f._ = { f._ = {
string: function(stmt, ndx, val, asBlob){ string: function(stmt, ndx, val, asBlob){
const bytes = intArrayFromString(val,true); const bytes = intArrayFromString(val,true);
const pStr = SQM.allocate(bytes, ALLOC_NORMAL); const pStr = api.wasm.allocate(bytes);
stmt._allocs.push(pStr); stmt._allocs.push(pStr);
const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text; const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
return func(stmt._pStmt, ndx, pStr, bytes.length, 0); return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
@ -1038,7 +1166,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
toss("Binding a value as a blob requires", toss("Binding a value as a blob requires",
"that it have a length member."); "that it have a length member.");
} }
const pBlob = SQM.allocate(val, ALLOC_NORMAL); const pBlob = api.wasm.allocate(val);
stmt._allocs.push(pBlob); stmt._allocs.push(pBlob);
rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0); rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
} }
@ -1054,7 +1182,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
const freeBindMemory = function(stmt){ const freeBindMemory = function(stmt){
let m; let m;
while(undefined !== (m = stmt._allocs.pop())){ while(undefined !== (m = stmt._allocs.pop())){
SQM._free(m); api.wasm._free(m);
} }
return stmt; return stmt;
}; };
@ -1481,7 +1609,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
if(self === self.window){ if(self === self.window){
/* This is running in the main window thread, so we're done. */ /* This is running in the main window thread, so we're done. */
setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0); postMessage({type:'sqlite3-api',data:'loaded'});
return; return;
} }
/****************************************************************** /******************************************************************
@ -1489,14 +1617,15 @@ Module.postRun.push(function(namespace/*the module object, the target for
in Worker threads. in Worker threads.
******************************************************************/ ******************************************************************/
/* /**
UNDER CONSTRUCTION UNDER CONSTRUCTION
We need an API which can proxy the DB API via a Worker message 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 interface. The primary quirky factor in such an API is that we
cannot pass callback functions between the window thread and a cannot pass callback functions between the window thread and a
worker thread, so we have to receive all db results via worker thread, so we have to receive all db results via
asynchronous message-passing. asynchronous message-passing. That requires an asychronous API
with a distinctly different shape that the main OO API.
Certain important considerations here include: Certain important considerations here include:
@ -1777,5 +1906,5 @@ Module.postRun.push(function(namespace/*the module object, the target for
wState.post(evType, response, wMsgHandler.xfer); wState.post(evType, response, wMsgHandler.xfer);
}; };
setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0); postMessage({type:'sqlite3-api',data:'loaded'});
}); })/*postRun.push(...)*/;

View File

@ -27,7 +27,10 @@
const api = sqlite3.api; const api = sqlite3.api;
log("Basic sanity tests..."); log("Basic sanity tests...");
T.assert(db._pDb); T.assert(db._pDb);
let st = db.prepare("select 3 as a"); let st = db.prepare(
new TextEncoder('utf-8').encode("select 3 as a")
/* Testing handling of Uint8Array input */
);
//log("statement =",st); //log("statement =",st);
T.assert(st._pStmt) T.assert(st._pStmt)
.assert(!st._mayGet) .assert(!st._mayGet)
@ -47,6 +50,7 @@
.assert(3.0 === st.get(0,api.SQLITE_FLOAT)) .assert(3.0 === st.get(0,api.SQLITE_FLOAT))
.assert(3.0 === st.getFloat(0)) .assert(3.0 === st.getFloat(0))
.assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array) .assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array)
.assert(1===st.get(0,api.SQLITE_BLOB).length)
.assert(st.getBlob(0) instanceof Uint8Array) .assert(st.getBlob(0) instanceof Uint8Array)
.assert(3 === st.get([])[0]) .assert(3 === st.get([])[0])
.assert(3 === st.get({}).a) .assert(3 === st.get({}).a)
@ -72,7 +76,7 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
//log("Exec'd SQL:", list); //log("Exec'd SQL:", list);
let counter = 0, colNames = []; let counter = 0, colNames = [];
list.length = 0; list.length = 0;
db.exec("SELECT a a, b b FROM t",{ db.exec(new TextEncoder('utf-8').encode("SELECT a a, b b FROM t"),{
rowMode: 'object', rowMode: 'object',
resultRows: list, resultRows: list,
columnNames: colNames, columnNames: colNames,

View File

@ -1,5 +1,5 @@
C Fix\sa\sharmless\sUBSAN\swarning\sassociated\swith\sPRAGMA\sschema_version\nfound\sby\sOSSFuzz. C wasm\sbinding:\sconsolidated\sthe\stwo\ssqlite3_prepare_v2()\sbindings\sbehind\sa\ssingle\sdispathcer.\sVarious\sinternal\scleanups\sand\srefactoring.\sBranched\sbecause\strunk\sis\sin\spencils-down\smode\sfor\spending\s3.39\srelease.
D 2022-06-24T12:56:48.588 D 2022-06-25T02:37:57.122
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -65,11 +65,11 @@ F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba83
F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8 F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/sqlite3-api.js ccf4bd0c1c5bbb3be3469573423d6c53991941bec497eac63e9f17ea13bf8952 F ext/fiddle/sqlite3-api.js aff4fe5583d02cb7207cc27a92d2cc708fca8a0eba14339f519059298f6bbc5e
F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421 F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421
F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
F ext/fiddle/testing1.js e2fa02ac8adbd21c69bc50cfcb79bfc26af0d30a8d6b95ac473a17e0dc9de733 F ext/fiddle/testing1.js 2ba8d14b335cdfc694757c25a3ffcfa76f4696bb21187db5c12ea6e505c44af0
F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4 F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4
F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@ -1978,8 +1978,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P db5266dec601a9513bc8dd09a9f8bb4aef55b780d22610946099e8edd4836587 P e93fd170ce4ae91d572c46d03f68f55d00091d0188030517455017d90d212587
R 09efcead2b1be4144ef279312bf3e194 R 3edce7228a2d729bf332a89ed3af6305
U drh T *branch * wasm-cleanups
Z 1fc91f4cca35906afdee86eebc313610 T *sym-wasm-cleanups *
T -sym-trunk * Cancelled\sby\sbranch.
U stephan
Z e1adc8db1d84e5f34db3da1c68e91f04
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
e93fd170ce4ae91d572c46d03f68f55d00091d0188030517455017d90d212587 ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c