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:
@ -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(...)*/;
|
||||||
|
@ -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,
|
||||||
|
19
manifest
19
manifest
@ -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.
|
||||||
|
@ -1 +1 @@
|
|||||||
e93fd170ce4ae91d572c46d03f68f55d00091d0188030517455017d90d212587
|
ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c
|
Reference in New Issue
Block a user