mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
WASM: added bindings for sqlite3_compileoption_get/used(), moved OO #1 into sqlite3-api.js since it can only be used from the same thread as that API and separating them complicates client-side use. Started adding test utilities and tests for the OO1 API.
FossilOrigin-Name: f3bc0328c87cac7d50513b0f13576d8fe7b411396f19c08fbe7e7c657b33cfbf
This commit is contained in:
@ -3,6 +3,7 @@ _sqlite3_bind_double
|
|||||||
_sqlite3_bind_int
|
_sqlite3_bind_int
|
||||||
_sqlite3_bind_int64
|
_sqlite3_bind_int64
|
||||||
_sqlite3_bind_null
|
_sqlite3_bind_null
|
||||||
|
_sqlite3_bind_parameter_count
|
||||||
_sqlite3_bind_parameter_index
|
_sqlite3_bind_parameter_index
|
||||||
_sqlite3_bind_text
|
_sqlite3_bind_text
|
||||||
_sqlite3_changes
|
_sqlite3_changes
|
||||||
@ -16,6 +17,8 @@ _sqlite3_column_double
|
|||||||
_sqlite3_column_name
|
_sqlite3_column_name
|
||||||
_sqlite3_column_text
|
_sqlite3_column_text
|
||||||
_sqlite3_column_type
|
_sqlite3_column_type
|
||||||
|
_sqlite3_compileoption_get
|
||||||
|
_sqlite3_compileoption_used
|
||||||
_sqlite3_create_function_v2
|
_sqlite3_create_function_v2
|
||||||
_sqlite3_data_count
|
_sqlite3_data_count
|
||||||
_sqlite3_db_filename
|
_sqlite3_db_filename
|
||||||
|
@ -10,21 +10,31 @@
|
|||||||
|
|
||||||
***********************************************************************
|
***********************************************************************
|
||||||
|
|
||||||
This file is intended to be loaded after loading
|
This file is intended to be loaded after loading sqlite3.wasm. It
|
||||||
sqlite3-module.wasm. It sets one of any number of potential
|
sets one of any number of potential bindings using that API, this
|
||||||
bindings using that API, this one as closely matching the C-native
|
one as closely matching the C-native API as is feasible.
|
||||||
API as is feasible.
|
|
||||||
|
|
||||||
Note that this file is not named sqlite3.js because that file gets
|
Note that this file is not named sqlite3.js because that file gets
|
||||||
generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
|
generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
|
||||||
|
|
||||||
The API gets installed as self.sqlite3, where self is expected to be
|
The API gets installed as self.sqlite3, where self is expected to be
|
||||||
either the global window or Worker object.
|
either the global window or Worker object. In addition, a higher-level
|
||||||
|
OO API is installed as self.SQLite3.
|
||||||
|
|
||||||
Because using this API properly requires some degree of WASM-related
|
Potential TODO: instead of exporting 2 symbols, export only SQLite3
|
||||||
magic, it is not recommended that this API be used as-is in
|
as {api: sqlite3, oo1: SQLite3}. The way we export this module is
|
||||||
client-level code, but instead is intended to be used as a basis for
|
not _really_ modern-JS-friendly because it exports global symbols
|
||||||
APIs more appropriate for high-level client code.
|
(which is admittedly poor form). Exporting it "cleanly" requires
|
||||||
|
using a module loader in all client code. As there are several
|
||||||
|
different approaches, none of which this developer is currently
|
||||||
|
truly familiar with, the current approach will have to do for the
|
||||||
|
time being.
|
||||||
|
|
||||||
|
Because using the low-level API properly requires some degree of
|
||||||
|
WASM-related magic, it is not recommended that that API be used
|
||||||
|
as-is in client-level code. Rather, client code should use the
|
||||||
|
higher-level OO API or write such a wrapper on top of the
|
||||||
|
lower-level API.
|
||||||
|
|
||||||
This file installs namespace.sqlite3, where namespace is `self`,
|
This file installs namespace.sqlite3, where namespace is `self`,
|
||||||
meaning either the global window or worker, depending on where this
|
meaning either the global window or worker, depending on where this
|
||||||
@ -74,8 +84,23 @@
|
|||||||
/* It is important that the following integer values match
|
/* It is important that the following integer values match
|
||||||
those from the C code. Ideally we could fetch them from the
|
those from the C code. Ideally we could fetch them from the
|
||||||
C API, e.g., in the form of a JSON object, but getting that
|
C API, e.g., in the form of a JSON object, but getting that
|
||||||
JSON string constructed within our current confised is
|
JSON string constructed within our current confines is
|
||||||
currently not worth the effort. */
|
currently not worth the effort.
|
||||||
|
|
||||||
|
Reminder to self: we could probably do so by adding the
|
||||||
|
proverbial level of indirection, calling in to C to get it,
|
||||||
|
and having that C func call an
|
||||||
|
emscripten-installed/JS-implemented library function which
|
||||||
|
builds the result object:
|
||||||
|
|
||||||
|
const obj = {};
|
||||||
|
sqlite3__get_enum(function(key,val){
|
||||||
|
obj[key] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
but whether or not we can pass a function that way, via a
|
||||||
|
(void*) is as yet unknown.
|
||||||
|
*/
|
||||||
/* Minimum subset of sqlite result codes we'll need. */
|
/* Minimum subset of sqlite result codes we'll need. */
|
||||||
SQLITE_OK: 0,
|
SQLITE_OK: 0,
|
||||||
SQLITE_ROW: 100,
|
SQLITE_ROW: 100,
|
||||||
@ -108,6 +133,7 @@
|
|||||||
["sqlite3_bind_int","number",["number", "number", "number"]],
|
["sqlite3_bind_int","number",["number", "number", "number"]],
|
||||||
["sqlite3_bind_int64","number",["number", "number", "number"]],
|
["sqlite3_bind_int64","number",["number", "number", "number"]],
|
||||||
["sqlite3_bind_null","void",["number"]],
|
["sqlite3_bind_null","void",["number"]],
|
||||||
|
["sqlite3_bind_parameter_count", "number", ["number"]],
|
||||||
["sqlite3_bind_parameter_index","number",["number", "string"]],
|
["sqlite3_bind_parameter_index","number",["number", "string"]],
|
||||||
["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]],
|
["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]],
|
||||||
["sqlite3_changes", "number", ["number"]],
|
["sqlite3_changes", "number", ["number"]],
|
||||||
@ -121,6 +147,8 @@
|
|||||||
["sqlite3_column_name","string",["number", "number"]],
|
["sqlite3_column_name","string",["number", "number"]],
|
||||||
["sqlite3_column_text","string",["number", "number"]],
|
["sqlite3_column_text","string",["number", "number"]],
|
||||||
["sqlite3_column_type","number",["number", "number"]],
|
["sqlite3_column_type","number",["number", "number"]],
|
||||||
|
["sqlite3_compileoption_get", "string", ["number"]],
|
||||||
|
["sqlite3_compileoption_used", "number", ["string"]],
|
||||||
["sqlite3_create_function_v2", "number",
|
["sqlite3_create_function_v2", "number",
|
||||||
["number", "string", "number", "number","number",
|
["number", "string", "number", "number","number",
|
||||||
"number", "number", "number", "number"]],
|
"number", "number", "number", "number"]],
|
||||||
@ -162,5 +190,447 @@
|
|||||||
api[k] = cwrap.apply(this, a);
|
api[k] = cwrap.apply(this, a);
|
||||||
});
|
});
|
||||||
//console.debug("libversion =",api.sqlite3_libversion());
|
//console.debug("libversion =",api.sqlite3_libversion());
|
||||||
namespace.sqlite3 = api;
|
|
||||||
|
/* What follows is colloquially known as "OO API #1". It is a
|
||||||
|
binding of the sqlite3 API which is designed to be run within
|
||||||
|
the same thread (main or worker) as the one in which the
|
||||||
|
sqlite3 WASM binding was initialized. This wrapper cannot use
|
||||||
|
the sqlite3 binding if, e.g., the wrapper is in the main thread
|
||||||
|
and the sqlite3 API is in a worker. */
|
||||||
|
/* memory for use in some pointer-passing routines */
|
||||||
|
const pPtrArg = stackAlloc(4);
|
||||||
|
const toss = function(){
|
||||||
|
throw new Error(Array.prototype.join.call(arguments, ' '));
|
||||||
|
};
|
||||||
|
|
||||||
|
const sqlite3/*canonical name*/ = S/*convenience alias*/ = api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The DB class wraps a sqlite3 db handle.
|
||||||
|
*/
|
||||||
|
const DB = function(name/*TODO: openMode flags*/){
|
||||||
|
if(!name) name = ':memory:';
|
||||||
|
else if('string'!==typeof name){
|
||||||
|
toss("TODO: support blob image of db here.");
|
||||||
|
}
|
||||||
|
this.checkRc(S.sqlite3_open(name, pPtrArg));
|
||||||
|
this.pDb = getValue(pPtrArg, "i32");
|
||||||
|
this.filename = name;
|
||||||
|
this._statements = {/*map of open Stmt _pointers_*/};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Internal-use enum for mapping JS types to DB-bindable types.
|
||||||
|
These do not (and need not) line up with the SQLITE_type
|
||||||
|
values. All values in this enum must be truthy and distinct
|
||||||
|
but they need not be numbers.
|
||||||
|
*/
|
||||||
|
const BindTypes = {
|
||||||
|
null: 1,
|
||||||
|
number: 2,
|
||||||
|
string: 3,
|
||||||
|
boolean: 4,
|
||||||
|
blob: 5
|
||||||
|
};
|
||||||
|
BindTypes['undefined'] == BindTypes.null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class wraps sqlite3_stmt. Calling this constructor
|
||||||
|
directly will trigger an exception. Use DB.prepare() to create
|
||||||
|
new instances.
|
||||||
|
*/
|
||||||
|
const Stmt = function(){
|
||||||
|
if(BindTypes!=arguments[2]){
|
||||||
|
toss("Do not call the Stmt constructor directly. Use DB.prepare().");
|
||||||
|
}
|
||||||
|
this.db = arguments[0];
|
||||||
|
this.pStmt = arguments[1];
|
||||||
|
this.columnCount = S.sqlite3_column_count(this.pStmt);
|
||||||
|
this.parameterCount = S.sqlite3_bind_parameter_count(this.pStmt);
|
||||||
|
this._allocs = [/*list of alloc'd memory blocks for bind() values*/]
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Throws if the given DB has been closed, else it is returned. */
|
||||||
|
const affirmDbOpen = function(db){
|
||||||
|
if(!db.pDb) toss("DB has been closed.");
|
||||||
|
return db;
|
||||||
|
};
|
||||||
|
|
||||||
|
DB.prototype = {
|
||||||
|
/**
|
||||||
|
Expects to be given an sqlite3 API result code. If it is
|
||||||
|
falsy, this function returns this object, else it throws an
|
||||||
|
exception with an error message from sqlite3_errmsg(),
|
||||||
|
using this object's db handle.
|
||||||
|
*/
|
||||||
|
checkRc: function(sqliteResultCode){
|
||||||
|
if(!sqliteResultCode) return this;
|
||||||
|
toss(S.sqlite3_errmsg(this.pDb) || "Unknown db error.");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Finalizes all open statements and closes this database
|
||||||
|
connection. This is a no-op if the db has already been
|
||||||
|
closed.
|
||||||
|
*/
|
||||||
|
close: function(){
|
||||||
|
if(this.pDb){
|
||||||
|
let s;
|
||||||
|
const that = this;
|
||||||
|
Object.keys(this._statements).forEach(function(k,s){
|
||||||
|
delete that._statements[k];
|
||||||
|
if(s && s.pStmt) s.finalize();
|
||||||
|
});
|
||||||
|
S.sqlite3_close_v2(this.pDb);
|
||||||
|
delete this.pDb;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Similar to this.filename but will return NULL for
|
||||||
|
special names like ":memory:". Not of much use until
|
||||||
|
we have filesystem support. Throws if the DB has
|
||||||
|
been closed. If passed an argument it then it will return
|
||||||
|
the filename of the ATTACHEd db with that name, else it assumes
|
||||||
|
a name of `main`.
|
||||||
|
*/
|
||||||
|
fileName: function(dbName){
|
||||||
|
return S.sqlite3_db_filename(affirmDbOpen(this).pDb, dbName||"main");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Compiles the given SQL and returns a prepared Stmt. This is
|
||||||
|
the only way to create new Stmt objects. Throws on error.
|
||||||
|
*/
|
||||||
|
prepare: function(sql){
|
||||||
|
affirmDbOpen(this);
|
||||||
|
setValue(pPtrArg,0,"i32");
|
||||||
|
this.checkRc(S.sqlite3_prepare_v2(this.pDb, sql, -1, pPtrArg, null));
|
||||||
|
const pStmt = getValue(pPtrArg, "i32");
|
||||||
|
if(!pStmt) toss("Empty SQL is not permitted.");
|
||||||
|
const stmt = new Stmt(this, pStmt, BindTypes);
|
||||||
|
this._statements[pStmt] = stmt;
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Returns an opaque truthy value from the BindTypes
|
||||||
|
enum if v's type is a valid bindable type, else
|
||||||
|
returns a falsy value. */
|
||||||
|
const isSupportedBindType = function(v){
|
||||||
|
let t = BindTypes[null===v ? 'null' : typeof v];
|
||||||
|
if(t) return t;
|
||||||
|
// TODO: handle buffer/blob types.
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If isSupportedBindType(v) returns a truthy value, this
|
||||||
|
function returns that value, else it throws.
|
||||||
|
*/
|
||||||
|
const affirmSupportedBindType = function(v){
|
||||||
|
const t = isSupportedBindType(v);
|
||||||
|
if(t) return t;
|
||||||
|
toss("Unsupport bind() argument type.");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
If key is a number and within range of stmt's bound parameter
|
||||||
|
count, key is returned.
|
||||||
|
|
||||||
|
If key is not a number then it is checked against named
|
||||||
|
parameters. If a match is found, its index is returned.
|
||||||
|
|
||||||
|
Else it throws.
|
||||||
|
*/
|
||||||
|
const indexOfParam = function(stmt,key){
|
||||||
|
const n = ('number'===typeof key)
|
||||||
|
? key : S.sqlite3_bind_parameter_index(stmt.pStmt, key);
|
||||||
|
if(0===n || (n===key && (n!==(n|0)/*floating point*/))){
|
||||||
|
toss("Invalid bind() parameter name: "+key);
|
||||||
|
}
|
||||||
|
else if(n<1 || n>=stmt.parameterCount) toss("Bind index",key,"is out of range.");
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a single bound parameter value on the given stmt at the
|
||||||
|
given index (numeric or named) using the given bindType (see
|
||||||
|
the BindTypes enum) and value. Throws on error. Returns stmt on
|
||||||
|
success.
|
||||||
|
*/
|
||||||
|
const bindOne = function(stmt,ndx,bindType,val){
|
||||||
|
affirmSupportedBindType(val);
|
||||||
|
ndx = indexOfParam(stmt,ndx);
|
||||||
|
let rc = 0;
|
||||||
|
switch(bindType){
|
||||||
|
case BindType.null:
|
||||||
|
rc = S.sqlite3_bind_null(stmt.pStmt, ndx);
|
||||||
|
break;
|
||||||
|
case BindType.string:{
|
||||||
|
const bytes = intArrayFromString(string,false);
|
||||||
|
const pStr = allocate(bytes, ALLOC_NORMAL);
|
||||||
|
stmt._allocs.push(pStr);
|
||||||
|
rc = S.sqlite3_bind_text(stmt.pStmt, ndx, pStr,
|
||||||
|
bytes.length, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BindType.number: {
|
||||||
|
const m = ((val === (val|0))
|
||||||
|
? (val>0xefffffff
|
||||||
|
? S.sqlite3_bind_int64
|
||||||
|
: S.sqlite3_bind_int)
|
||||||
|
: S.sqlite3_bind_double);
|
||||||
|
rc = m(stmt.pStmt, ndx, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BindType.boolean:
|
||||||
|
rc = S.sqlite3_bind_int(stmt.pStmt, ndx, val ? 1 : 0);
|
||||||
|
break;
|
||||||
|
case BindType.blob:
|
||||||
|
default: toss("Unsupported bind() argument type.");
|
||||||
|
}
|
||||||
|
if(rc) stmt.db.checkRc(rc);
|
||||||
|
return stmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Throws if the given Stmt has been finalized, else
|
||||||
|
it is returned. */
|
||||||
|
const affirmStmtOpen = function(stmt){
|
||||||
|
if(!stmt.pStmt) toss("Stmt has been closed.");
|
||||||
|
return stmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Frees any memory explicitly allocated for the given
|
||||||
|
Stmt object. Returns stmt. */
|
||||||
|
const freeBindMemory = function(stmt){
|
||||||
|
let m;
|
||||||
|
while(undefined !== (m = stmt._allocs.pop())){
|
||||||
|
_free(m);
|
||||||
|
}
|
||||||
|
return stmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
Stmt.prototype = {
|
||||||
|
/**
|
||||||
|
"Finalizes" this statement. This is a no-op if the
|
||||||
|
statement has already been finalizes. Returns
|
||||||
|
undefined. Most methods in this class will throw if called
|
||||||
|
after this is.
|
||||||
|
*/
|
||||||
|
finalize: function(){
|
||||||
|
if(this.pStmt){
|
||||||
|
freeBindMemory(this);
|
||||||
|
delete this.db._statements[this.pStmt];
|
||||||
|
S.sqlite3_finalize(this.pStmt);
|
||||||
|
delete this.pStmt;
|
||||||
|
delete this.db;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** Clears all bound values. Returns this object.
|
||||||
|
Throws if this statement has been finalized. */
|
||||||
|
clearBindings: function(){
|
||||||
|
freeBindMemory(affirmStmtOpen(this));
|
||||||
|
S.sqlite3_clear_bindings(this.pStmt);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Resets this statement so that it may be step()ed again
|
||||||
|
from the beginning. Returns this object. Throws if this
|
||||||
|
statement has been finalized.
|
||||||
|
|
||||||
|
If passed a truthy argument then this.clearBindings() is
|
||||||
|
also called, otherwise any existing bindings, along with
|
||||||
|
any memory allocated for them, are retained.
|
||||||
|
*/
|
||||||
|
reset: function(alsoClearBinds){
|
||||||
|
if(alsoClearBinds) this.clearBindings();
|
||||||
|
S.sqlite3_reset(affirmStmtOpen(this).pStmt);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Binds one or more values to its bindable parameters. It
|
||||||
|
accepts 1 or 2 arguments:
|
||||||
|
|
||||||
|
If passed a single argument, it must be either an array, an
|
||||||
|
object, or a value of a bindable type (see below).
|
||||||
|
|
||||||
|
If passed 2 arguments, the first one is the 1-based bind
|
||||||
|
index or bindable parameter name and the second one must be
|
||||||
|
a value of a bindable type.
|
||||||
|
|
||||||
|
Bindable value types:
|
||||||
|
|
||||||
|
- null or undefined is bound as NULL.
|
||||||
|
|
||||||
|
- Numbers are bound as either doubles or integers: int64 if
|
||||||
|
they are larger than 0xEFFFFFFF, else int32. Booleans are
|
||||||
|
bound as integer 0 or 1. Note that doubles with no
|
||||||
|
fractional part are bound as integers. It is not expected
|
||||||
|
that that distinction is significant for the majority of
|
||||||
|
clients due to sqlite3's data typing model. This API does
|
||||||
|
not currently support the BigInt type.
|
||||||
|
|
||||||
|
- Strings are bound as strings (use bindAsBlob() to force
|
||||||
|
blob binding).
|
||||||
|
|
||||||
|
- buffers (blobs) are currently TODO but will be bound as
|
||||||
|
blobs.
|
||||||
|
|
||||||
|
If passed an array, each element of the array is bound at
|
||||||
|
the parameter index equal to the array index plus 1
|
||||||
|
(because arrays are 0-based but binding is 1-based).
|
||||||
|
|
||||||
|
If passed an object, each object key is treated as a
|
||||||
|
bindable parameter name. The object keys _must_ match any
|
||||||
|
bindable parameter names, including any `$`, `@`, or `:`
|
||||||
|
prefix. Because `$` is a legal identifier chararacter in
|
||||||
|
JavaScript, that is the suggested prefix for bindable
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
It returns this object on success and throws on
|
||||||
|
error. Errors include:
|
||||||
|
|
||||||
|
- Any bind index is out of range, a named bind parameter
|
||||||
|
does not match, or this statement has no bindable
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
- Any value to bind is of an unsupported type.
|
||||||
|
|
||||||
|
- Passed no arguments or more than two.
|
||||||
|
|
||||||
|
- The statement has been finalized.
|
||||||
|
*/
|
||||||
|
bind: function(/*[ndx,] value*/){
|
||||||
|
if(!this.parameterCount){
|
||||||
|
toss("This statement has no bindable parameters.");
|
||||||
|
}
|
||||||
|
let ndx, arg;
|
||||||
|
switch(arguments.length){
|
||||||
|
case 1: ndx = 1; arg = arguments[0]; break;
|
||||||
|
case 2: ndx = arguments[0]; arg = arguments[1]; break;
|
||||||
|
default: toss("Invalid bind() arguments.");
|
||||||
|
}
|
||||||
|
affirmStmtOpen(this);
|
||||||
|
if(null===arg || undefined===arg){
|
||||||
|
/* bind NULL */
|
||||||
|
return bindOne(this, ndx, BindType.null, arg);
|
||||||
|
}
|
||||||
|
else if(Array.isArray(arg)){
|
||||||
|
/* bind each entry by index */
|
||||||
|
if(1!==arguments.length){
|
||||||
|
toss("When binding an array, an index argument is not permitted.");
|
||||||
|
}
|
||||||
|
arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
else if('object'===typeof arg/*null was checked above*/){
|
||||||
|
/* bind by name */
|
||||||
|
if(1!==arguments.length){
|
||||||
|
toss("When binding an object, an index argument is not permitted.");
|
||||||
|
}
|
||||||
|
Object.keys(arg)
|
||||||
|
.forEach(k=>bindOne(this, k,
|
||||||
|
affirmSupportedBindType(arg[k]),
|
||||||
|
arg[k]));
|
||||||
|
return this;
|
||||||
|
}else{
|
||||||
|
return bindOne(this, ndx,
|
||||||
|
affirmSupportedBindType(arg), arg);
|
||||||
|
}
|
||||||
|
toss("Should not reach this point.");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Special case of bind() which binds the given value
|
||||||
|
using the BLOB binding mechanism instead of the default
|
||||||
|
selected one for the value. The ndx may be a numbered
|
||||||
|
or named bind index. The value must be of type string,
|
||||||
|
buffer, or null/undefined (both treated as null).
|
||||||
|
|
||||||
|
If passed a single argument, a bind index of 1 is assumed.
|
||||||
|
*/
|
||||||
|
bindAsBlob: function(ndx,arg){
|
||||||
|
affirmStmtOpen(this);
|
||||||
|
if(1===arguments.length){
|
||||||
|
ndx = 1;
|
||||||
|
arg = arguments[0];
|
||||||
|
}
|
||||||
|
const t = affirmSupportedBindType(arg);
|
||||||
|
if(BindTypes.string !== t && BindTypes.blob !== t
|
||||||
|
&& BindTypes.null !== t){
|
||||||
|
toss("Invalid value type for bindAsBlob()");
|
||||||
|
}
|
||||||
|
return bindOne(this, ndx, BindType.blob, arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** OO binding's namespace. */
|
||||||
|
const SQLite3 = {
|
||||||
|
version: {
|
||||||
|
lib: sqlite3.sqlite3_libversion(),
|
||||||
|
ooApi: "0.0.1"
|
||||||
|
},
|
||||||
|
DB,
|
||||||
|
Stmt,
|
||||||
|
/**
|
||||||
|
Reports whether a given compile-time option, named by the
|
||||||
|
given argument.
|
||||||
|
|
||||||
|
If optName is an array then it is expected to be a list of
|
||||||
|
compilation options and this function returns an object
|
||||||
|
which maps each such option to true or false. That object
|
||||||
|
is returned.
|
||||||
|
|
||||||
|
If optName is an object, its keys are expected to be
|
||||||
|
compilation options and this function sets each entry to
|
||||||
|
true or false. That object is returned.
|
||||||
|
|
||||||
|
If passed no arguments then it returns an object mapping
|
||||||
|
all known compilation options to their compile-time values,
|
||||||
|
or true if the are defined with no value.
|
||||||
|
|
||||||
|
In all other cases it returns true if the option was active
|
||||||
|
when when compiling the sqlite3 module, else false.
|
||||||
|
|
||||||
|
Compile-time option names may optionally include their
|
||||||
|
"SQLITE_" prefix. When it returns an object of all options,
|
||||||
|
the prefix is elided.
|
||||||
|
*/
|
||||||
|
compileOptionUsed: function f(optName){
|
||||||
|
if(!arguments.length){
|
||||||
|
if(!f._opt){
|
||||||
|
f._rx = /^([^=]+)=(.+)/;
|
||||||
|
f._rxInt = /^-?\d+/;
|
||||||
|
f._opt = function(opt, rv){
|
||||||
|
const m = f._rx.exec(opt);
|
||||||
|
rv[0] = (m ? m[1] : opt);
|
||||||
|
rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const rc = {}, ov = [0,0];
|
||||||
|
let i = 0;
|
||||||
|
while((k = S.sqlite3_compileoption_get(i++))){
|
||||||
|
f._opt(k,ov);
|
||||||
|
rc[ov[0]] = ov[1];
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
else if(Array.isArray(optName)){
|
||||||
|
const rc = {};
|
||||||
|
optName.forEach((v)=>{
|
||||||
|
rc[v] = S.sqlite3_compileoption_used(v);
|
||||||
|
});
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
else if('object' === typeof optName){
|
||||||
|
Object.keys(optName).forEach((k)=> {
|
||||||
|
optName[k] = S.sqlite3_compileoption_used(k);
|
||||||
|
});
|
||||||
|
return optName;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
'string'===typeof optName
|
||||||
|
) ? !!S.sqlite3_compileoption_used(optName) : false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace.sqlite3 = sqlite3;
|
||||||
|
namespace.SQLite3 = SQLite3;
|
||||||
})(self/*worker or window*/);
|
})(self/*worker or window*/);
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
const statusElement = E('#module-status');
|
const statusElement = E('#module-status');
|
||||||
const progressElement = E('#module-progress');
|
const progressElement = E('#module-progress');
|
||||||
const spinnerElement = E('#module-spinner');
|
const spinnerElement = E('#module-spinner');
|
||||||
|
|
||||||
self.Module = {
|
self.Module = {
|
||||||
/* ^^^ cannot declare that const because fiddle-module.js
|
/* ^^^ cannot declare that const because fiddle-module.js
|
||||||
(auto-generated) includes a decl for it and runs in this scope. */
|
(auto-generated) includes a decl for it and runs in this scope. */
|
||||||
@ -71,9 +70,10 @@
|
|||||||
: 'All downloads complete.');
|
: 'All downloads complete.');
|
||||||
},
|
},
|
||||||
/* Loads sqlite3-api.js and calls the given callback (if
|
/* Loads sqlite3-api.js and calls the given callback (if
|
||||||
provided), passing it the sqlite3 module. Whether this is
|
provided), passing it an object which contains the sqlite3
|
||||||
synchronous or async depends on whether it's run in the
|
and SQLite3 modules. Whether this is synchronous or async
|
||||||
main thread or a worker.*/
|
depends on whether it's run in the main thread or a
|
||||||
|
worker.*/
|
||||||
loadSqliteAPI: function(callback){
|
loadSqliteAPI: function(callback){
|
||||||
const theScript = 'sqlite3-api.js';
|
const theScript = 'sqlite3-api.js';
|
||||||
if(self.importScripts){/*worker*/
|
if(self.importScripts){/*worker*/
|
||||||
@ -88,9 +88,62 @@
|
|||||||
script.async = true;
|
script.async = true;
|
||||||
script.src = theScript;
|
script.src = theScript;
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if(callback) callback(self.sqlite3);
|
if(callback) callback({sqlite3:self.sqlite3,
|
||||||
|
SQLite3:self.SQLite3});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _expr = function(expr){
|
||||||
|
return (expr instanceof Function) ? expr() : !!expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Helpers for writing sqlite3-specific tests.
|
||||||
|
*/
|
||||||
|
self.SqliteTester = {
|
||||||
|
/** Running total of the number of tests run via
|
||||||
|
this API. */
|
||||||
|
counter: 0,
|
||||||
|
/** abort() if expr is false. If expr is a function, it
|
||||||
|
is called and its result is evaluated.
|
||||||
|
*/
|
||||||
|
assert: function(expr, msg){
|
||||||
|
++this.counter;
|
||||||
|
if(!_expr(expr)) abort(msg || "Assertion failed.");
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/** Identical to assert() but throws instead of calling
|
||||||
|
abort(). */
|
||||||
|
affirm: function(expr, msg){
|
||||||
|
++this.counter;
|
||||||
|
if(!_expr(expr)) throw new Error(msg || "Affirmation failed.");
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/** Calls f() and squelches any exception it throws. If it
|
||||||
|
does not throw, this function throws. */
|
||||||
|
mustThrow: function(f, msg){
|
||||||
|
++this.counter;
|
||||||
|
let err;
|
||||||
|
try{ f(); } catch(e){err=e;}
|
||||||
|
if(!err) throw new Error(msg || "Expected exception.");
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/** Throws if expr is truthy or expr is a function and expr()
|
||||||
|
returns truthy. */
|
||||||
|
throwIf: function(expr, msg){
|
||||||
|
++this.counter;
|
||||||
|
if(_expr(expr)) throw new Error(msg || "throwIf() failed");
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/** Throws if expr is falsy or expr is a function and expr()
|
||||||
|
returns falsy. */
|
||||||
|
throwUnless: function(expr, msg){
|
||||||
|
++this.counter;
|
||||||
|
if(!_expr(expr)) throw new Error(msg || "throwUnless() failed");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
})(self/*window or worker*/);
|
})(self/*window or worker*/);
|
||||||
|
@ -12,382 +12,32 @@
|
|||||||
|
|
||||||
A basic test script for sqlite3-api.js.
|
A basic test script for sqlite3-api.js.
|
||||||
*/
|
*/
|
||||||
const setupAPI = function(S/*sqlite3 module*/){
|
|
||||||
|
|
||||||
/* memory for use in some pointer-passing routines */
|
const mainTest1 = function(namespace){
|
||||||
const pPtrArg = stackAlloc(4);
|
const S = namespace.sqlite3;
|
||||||
const dummyArg = {/*for restricting Stmt constructor to internal use*/};
|
const oo = namespace.SQLite3;
|
||||||
const toss = function(){
|
const T = self.SqliteTester;
|
||||||
throw new Error(Array.prototype.join.apply(arguments, ' '));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
The DB class wraps a sqlite3 db handle.
|
|
||||||
*/
|
|
||||||
const DB = function(name/*TODO: openMode flags*/){
|
|
||||||
if(!name) name = ':memory:';
|
|
||||||
else if('string'!==typeof name){
|
|
||||||
toss("TODO: support blob image of db here.");
|
|
||||||
}
|
|
||||||
this.checkRc(S.sqlite3_open(name, pPtrArg));
|
|
||||||
this.pDb = getValue(pPtrArg, "i32");
|
|
||||||
this.filename = name;
|
|
||||||
this._statements = {/*array of open Stmt _pointers_*/};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
This class wraps sqlite3_stmt. Calling this constructor
|
|
||||||
directly will trigger an exception. Use DB.prepare() to create
|
|
||||||
new instances.
|
|
||||||
*/
|
|
||||||
const Stmt = function(){
|
|
||||||
if(dummyArg!=arguments[2]){
|
|
||||||
toss("Do not call the Stmt constructor directly. Use DB.prepare().");
|
|
||||||
}
|
|
||||||
this.db = arguments[0];
|
|
||||||
this.pStmt = arguments[1];
|
|
||||||
this.columnCount = S.sqlite3_column_count(this.pStmt);
|
|
||||||
this._allocs = [/*list of alloc'd memory blocks for bind() values*/]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** Throws if the given DB has been closed, else it is returned. */
|
|
||||||
const affirmDbOpen = function(db){
|
|
||||||
if(!db.pDb) toss("DB has been closed.");
|
|
||||||
return db;
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype = {
|
|
||||||
/**
|
|
||||||
Expects to be given an sqlite3 API result code. If it is
|
|
||||||
falsy, this function returns this object, else it throws an
|
|
||||||
exception with an error message from sqlite3_errmsg(),
|
|
||||||
using this object's db handle.
|
|
||||||
*/
|
|
||||||
checkRc: function(sqliteResultCode){
|
|
||||||
if(!sqliteResultCode) return this;
|
|
||||||
toss(S.sqlite3_errmsg(this.pDb) || "Unknown db error.");
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
Finalizes all open statements and closes this database
|
|
||||||
connection. This is a no-op if the db has already been
|
|
||||||
closed.
|
|
||||||
*/
|
|
||||||
close: function(){
|
|
||||||
if(this.pDb){
|
|
||||||
let s;
|
|
||||||
while(undefined!==(s = this._statements.pop())){
|
|
||||||
if(s.pStmt) s.finalize();
|
|
||||||
}
|
|
||||||
S.sqlite3_close_v2(this.pDb);
|
|
||||||
delete this.pDb;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
Similar to this.filename but will return NULL for
|
|
||||||
special names like ":memory:". Not of much use until
|
|
||||||
we have filesystem support. Throws if the DB has
|
|
||||||
been closed. If passed an argument it then it will return
|
|
||||||
the filename of the ATTACHEd db with that name, else it assumes
|
|
||||||
a name of `main`.
|
|
||||||
*/
|
|
||||||
fileName: function(dbName){
|
|
||||||
return S.sqlite3_db_filename(affirmDbOpen(this).pDb, dbName||"main");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Compiles the given SQL and returns a prepared Stmt. This is
|
|
||||||
the only way to create new Stmt objects. Throws on error.
|
|
||||||
*/
|
|
||||||
prepare: function(sql){
|
|
||||||
affirmDbOpen(this);
|
|
||||||
setValue(pPtrArg,0,"i32");
|
|
||||||
this.checkRc(S.sqlite3_prepare_v2(this.pDb, sql, -1, pPtrArg, null));
|
|
||||||
const pStmt = getValue(pPtrArg, "i32");
|
|
||||||
if(!pStmt) toss("Empty SQL is not permitted.");
|
|
||||||
const stmt = new Stmt(this, pStmt, dummyArg);
|
|
||||||
this._statements[pStmt] = stmt;
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Internal-use enum for mapping JS types to DB-bindable types.
|
|
||||||
These do not (and need not) line up with the SQLITE_type
|
|
||||||
values. All values in this enum must be truthy and distinct
|
|
||||||
but they need not be numbers.
|
|
||||||
*/
|
|
||||||
const BindTypes = {
|
|
||||||
null: 1,
|
|
||||||
number: 2,
|
|
||||||
string: 3,
|
|
||||||
boolean: 4,
|
|
||||||
blob: 5
|
|
||||||
};
|
|
||||||
BindTypes['undefined'] == BindTypes.null;
|
|
||||||
|
|
||||||
/** Returns an opaque truthy value from the BindTypes
|
|
||||||
enum if v's type is a valid bindable type, else
|
|
||||||
returns a falsy value. */
|
|
||||||
const isSupportedBindType = function(v){
|
|
||||||
let t = BindTypes[null===v ? 'null' : typeof v];
|
|
||||||
if(t) return t;
|
|
||||||
// TODO: handle buffer/blob types.
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
If isSupportedBindType(v) returns a truthy value, this
|
|
||||||
function returns that value, else it throws.
|
|
||||||
*/
|
|
||||||
const affirmSupportedBindType = function(v){
|
|
||||||
const t = isSupportedBindType(v);
|
|
||||||
if(t) return t;
|
|
||||||
toss("Unsupport bind() argument type.");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
If key is a number and within range of stmt's bound parameter
|
|
||||||
count, key is returned.
|
|
||||||
|
|
||||||
If key is not a number then it is checked against named
|
|
||||||
parameters. If a match is found, its index is returned.
|
|
||||||
|
|
||||||
Else it throws.
|
|
||||||
*/
|
|
||||||
const indexOfParam = function(stmt,key){
|
|
||||||
const n = ('number'===typeof key)
|
|
||||||
? key : S.sqlite3_bind_parameter_index(stmt.pStmt, key);
|
|
||||||
if(0===n || (n===key && (n!==(n|0)/*floating point*/))){
|
|
||||||
toss("Invalid bind() parameter name: "+key);
|
|
||||||
}
|
|
||||||
else if(n>=stmt.columnCount) toss("Bind index",key,"is out of range.");
|
|
||||||
return n;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Binds a single bound parameter value on the given stmt at the
|
|
||||||
given index (numeric or named) using the given bindType (see
|
|
||||||
the BindTypes enum) and value. Throws on error. Returns stmt on
|
|
||||||
success.
|
|
||||||
*/
|
|
||||||
const bindOne = function(stmt,ndx,bindType,val){
|
|
||||||
affirmSupportedBindType(val);
|
|
||||||
ndx = indexOfParam(stmt,ndx);
|
|
||||||
let rc = 0;
|
|
||||||
switch(bindType){
|
|
||||||
case BindType.null:
|
|
||||||
rc = S.sqlite3_bind_null(stmt.pStmt, ndx);
|
|
||||||
break;
|
|
||||||
case BindType.string:{
|
|
||||||
const bytes = intArrayFromString(string,false);
|
|
||||||
const pStr = allocate(bytes, ALLOC_NORMAL);
|
|
||||||
stmt._allocs.push(pStr);
|
|
||||||
rc = S.sqlite3_bind_text(stmt.pStmt, ndx, pStr,
|
|
||||||
bytes.length, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BindType.number: {
|
|
||||||
const m = ((val === (val|0))
|
|
||||||
? (val>0xefffffff
|
|
||||||
? S.sqlite3_bind_int64
|
|
||||||
: S.sqlite3_bind_int)
|
|
||||||
: S.sqlite3_bind_double);
|
|
||||||
rc = m(stmt.pStmt, ndx, val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BindType.boolean:
|
|
||||||
rc = S.sqlite3_bind_int(stmt.pStmt, ndx, val ? 1 : 0);
|
|
||||||
break;
|
|
||||||
case BindType.blob:
|
|
||||||
default: toss("Unsupported bind() argument type.");
|
|
||||||
}
|
|
||||||
if(rc) stmt.db.checkRc(rc);
|
|
||||||
return stmt;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Throws if the given Stmt has been finalized, else
|
|
||||||
it is returned. */
|
|
||||||
const affirmStmtOpen = function(stmt){
|
|
||||||
if(!stmt.pStmt) toss("Stmt has been closed.");
|
|
||||||
return stmt;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Frees any memory explicitly allocated for the given
|
|
||||||
Stmt object. Returns stmt. */
|
|
||||||
const freeBindMemory = function(stmt){
|
|
||||||
let m;
|
|
||||||
while(undefined !== (m = stmt._allocs.pop())){
|
|
||||||
_free(m);
|
|
||||||
}
|
|
||||||
return stmt;
|
|
||||||
};
|
|
||||||
|
|
||||||
Stmt.prototype = {
|
|
||||||
/**
|
|
||||||
"Finalizes" this statement. This is a no-op if the
|
|
||||||
statement has already been finalizes. Returns
|
|
||||||
undefined. Most methods in this class will throw if called
|
|
||||||
after this is.
|
|
||||||
*/
|
|
||||||
finalize: function(){
|
|
||||||
if(this.pStmt){
|
|
||||||
freeBindMemory(this);
|
|
||||||
S.sqlite3_finalize(this.pStmt);
|
|
||||||
delete this.pStmt;
|
|
||||||
delete this.db;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/** Clears all bound values. Returns this object.
|
|
||||||
Throws if this statement has been finalized. */
|
|
||||||
clearBindings: function(){
|
|
||||||
freeBindMemory(affirmStmtOpen(this));
|
|
||||||
S.sqlite3_clear_bindings(this.pStmt);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
Resets this statement so that it may be step()ed again
|
|
||||||
from the beginning. Returns this object. Throws if this
|
|
||||||
statement has been finalized.
|
|
||||||
|
|
||||||
If passed a truthy argument then this.clearBindings() is
|
|
||||||
also called, otherwise any existing bindings, along with
|
|
||||||
any memory allocated for them, are retained.
|
|
||||||
*/
|
|
||||||
reset: function(alsoClearBinds){
|
|
||||||
if(alsoClearBinds) this.clearBindings();
|
|
||||||
S.sqlite3_reset(affirmStmtOpen(this).pStmt);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
Binds one or more values to its bindable parameters. It
|
|
||||||
accepts 1 or 2 arguments:
|
|
||||||
|
|
||||||
If passed a single argument, it must be either an array, an
|
|
||||||
object, or a value of a bindable type (see below).
|
|
||||||
|
|
||||||
If passed 2 arguments, the first one is the 1-based bind
|
|
||||||
index or bindable parameter name and the second one must be
|
|
||||||
a value of a bindable type.
|
|
||||||
|
|
||||||
Bindable value types:
|
|
||||||
|
|
||||||
- null or undefined is bound as NULL.
|
|
||||||
|
|
||||||
- Numbers are bound as either doubles or integers: int64 if
|
|
||||||
they are larger than 0xEFFFFFFF, else int32. Booleans are
|
|
||||||
bound as integer 0 or 1. Note that doubles with no
|
|
||||||
fractional part are bound as integers. It is not expected
|
|
||||||
that that distinction is significant for the majority of
|
|
||||||
clients due to sqlite3's data typing model. This API does
|
|
||||||
not currently support the BigInt type.
|
|
||||||
|
|
||||||
- Strings are bound as strings (use bindAsBlob() to force
|
|
||||||
blob binding).
|
|
||||||
|
|
||||||
- buffers (blobs) are currently TODO but will be bound as
|
|
||||||
blobs.
|
|
||||||
|
|
||||||
If passed an array, each element of the array is bound at
|
|
||||||
the parameter index equal to the array index plus 1
|
|
||||||
(because arrays are 0-based but binding is 1-based).
|
|
||||||
|
|
||||||
If passed an object, each object key is treated as a
|
|
||||||
bindable parameter name. The object keys _must_ match any
|
|
||||||
bindable parameter names, including any `$`, `@`, or `:`
|
|
||||||
prefix. Because `$` is a legal identifier chararacter in
|
|
||||||
JavaScript, that is the suggested prefix for bindable
|
|
||||||
parameters.
|
|
||||||
|
|
||||||
It returns this object on success and throws on
|
|
||||||
error. Errors include:
|
|
||||||
|
|
||||||
- Any bind index is out of range or a named bind parameter
|
|
||||||
does not match.
|
|
||||||
|
|
||||||
- Any value to bind is of an unsupported type.
|
|
||||||
|
|
||||||
- Passed no arguments or more than two.
|
|
||||||
|
|
||||||
- The statement has been finalized.
|
|
||||||
*/
|
|
||||||
bind: function(/*[ndx,] value*/){
|
|
||||||
let ndx, arg;
|
|
||||||
switch(arguments.length){
|
|
||||||
case 1: ndx = 1; arg = arguments[0]; break;
|
|
||||||
case 2: ndx = arguments[0]; arg = arguments[1]; break;
|
|
||||||
default: toss("Invalid bind() arguments.");
|
|
||||||
}
|
|
||||||
affirmStmtOpen(this);
|
|
||||||
if(null===arg || undefined===arg){
|
|
||||||
/* bind NULL */
|
|
||||||
return bindOne(this, ndx, BindType.null, arg);
|
|
||||||
}
|
|
||||||
else if(Array.isArray(arg)){
|
|
||||||
/* bind each entry by index */
|
|
||||||
if(1!==arguments.length){
|
|
||||||
toss("When binding an array, an index argument is not permitted.");
|
|
||||||
}
|
|
||||||
arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
else if('object'===typeof arg/*null was checked above*/){
|
|
||||||
/* bind by name */
|
|
||||||
if(1!==arguments.length){
|
|
||||||
toss("When binding an object, an index argument is not permitted.");
|
|
||||||
}
|
|
||||||
Object.keys(arg)
|
|
||||||
.forEach(k=>bindOne(this, k,
|
|
||||||
affirmSupportedBindType(arg[k]),
|
|
||||||
arg[k]));
|
|
||||||
return this;
|
|
||||||
}else{
|
|
||||||
return bindOne(this, ndx,
|
|
||||||
affirmSupportedBindType(arg), arg);
|
|
||||||
}
|
|
||||||
toss("Should not reach this point.");
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
Special case of bind() which binds the given value
|
|
||||||
using the BLOB binding mechanism instead of the default
|
|
||||||
selected one for the value. The ndx may be a numbered
|
|
||||||
or named bind index. The value must be of type string,
|
|
||||||
buffer, or null/undefined (both treated as null).
|
|
||||||
|
|
||||||
If passed a single argument, a bind index of 1 is assumed.
|
|
||||||
*/
|
|
||||||
bindAsBlob: function(ndx,arg){
|
|
||||||
affirmStmtOpen(this);
|
|
||||||
if(1===arguments.length){
|
|
||||||
ndx = 1;
|
|
||||||
arg = arguments[0];
|
|
||||||
}
|
|
||||||
const t = affirmSupportedBindType(arg);
|
|
||||||
if(BindTypes.string !== t && BindTypes.blob !== t
|
|
||||||
&& BindTypes.null !== t){
|
|
||||||
toss("Invalid value type for bindAsBlob()");
|
|
||||||
}
|
|
||||||
return bindOne(this, ndx, BindType.blob, arg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const SQLite3 = {
|
|
||||||
version: {
|
|
||||||
lib: S.sqlite3_libversion(),
|
|
||||||
ooApi: "0.0.1"
|
|
||||||
},
|
|
||||||
DB
|
|
||||||
};
|
|
||||||
return SQLite3;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mainTest1 = function(S/*sqlite3 module*/){
|
|
||||||
console.log("Loaded module:",S.sqlite3_libversion(),
|
console.log("Loaded module:",S.sqlite3_libversion(),
|
||||||
S.sqlite3_sourceid());
|
S.sqlite3_sourceid());
|
||||||
const oo = setupAPI(S);
|
|
||||||
|
|
||||||
const db = new oo.DB();
|
const db = new oo.DB();
|
||||||
console.log("DB:",db.filename);
|
const log = console.log.bind(console);
|
||||||
|
T.assert(db.pDb);
|
||||||
|
log("DB:",db.filename);
|
||||||
|
log("Build options:",oo.compileOptionUsed());
|
||||||
|
|
||||||
|
let st = db.prepare("select 1");
|
||||||
|
T.assert(st.pStmt);
|
||||||
|
log("statement =",st);
|
||||||
|
T.assert(st === db._statements[st.pStmt])
|
||||||
|
.assert(1===st.columnCount)
|
||||||
|
.assert(0===st.parameterCount)
|
||||||
|
.mustThrow(()=>st.bind(1,null));
|
||||||
|
|
||||||
|
let pId = st.pStmt;
|
||||||
|
st.finalize();
|
||||||
|
T.assert(!st.pStmt)
|
||||||
|
.assert(!db._statements[pId]);
|
||||||
|
log("Test count:",T.counter);
|
||||||
};
|
};
|
||||||
|
|
||||||
self/*window or worker*/.Module.onRuntimeInitialized = function(){
|
self/*window or worker*/.Module.onRuntimeInitialized = function(){
|
||||||
|
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
|||||||
C WASM\sOO\swrapper\s#1:\sprepare()\sand\sbind()\sAPIs\sare\sin\splace\sbut\sare\suntested,\spending\sfetch/get\sAPIs.
|
C WASM:\sadded\sbindings\sfor\ssqlite3_compileoption_get/used(),\smoved\sOO\s#1\sinto\ssqlite3-api.js\ssince\sit\scan\sonly\sbe\sused\sfrom\sthe\ssame\sthread\sas\sthat\sAPI\sand\sseparating\sthem\scomplicates\sclient-side\suse.\sStarted\sadding\stest\sutilities\sand\stests\sfor\sthe\sOO1\sAPI.
|
||||||
D 2022-05-22T14:07:44.577
|
D 2022-05-22T16:25:43.076
|
||||||
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
|
||||||
@ -56,7 +56,7 @@ F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0
|
|||||||
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
|
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
|
||||||
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
|
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
|
||||||
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f
|
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f
|
||||||
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 a3a2862941270ae5e2633d21cbf44979901c4b75efa42a452c15ef879b47ad2b
|
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 00553766051a038b1acd3992d04e50540d1284c3ea78bd11daa521383e57d653
|
||||||
F ext/fiddle/EXPORTED_RUNTIME_METHODS 91d5dcb0168ee056fa1a340cb8ab3c23d922622f8dad39d28919dd8af2b3ade0
|
F ext/fiddle/EXPORTED_RUNTIME_METHODS 91d5dcb0168ee056fa1a340cb8ab3c23d922622f8dad39d28919dd8af2b3ade0
|
||||||
F ext/fiddle/Makefile 9277c73e208b9c8093659256c9f07409c877e366480c7c22ec545ee345451d95
|
F ext/fiddle/Makefile 9277c73e208b9c8093659256c9f07409c877e366480c7c22ec545ee345451d95
|
||||||
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||||
@ -64,10 +64,10 @@ F ext/fiddle/fiddle-worker.js c22557b641b47fa1473d3465a4e69fe06b8b09b924955805a4
|
|||||||
F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c
|
F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c
|
||||||
F ext/fiddle/fiddle.js f9c79164428e96a5909532f18a8bc8f8c8ec4f738bfc09ad3d2a532c2400f9f0
|
F ext/fiddle/fiddle.js f9c79164428e96a5909532f18a8bc8f8c8ec4f738bfc09ad3d2a532c2400f9f0
|
||||||
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
|
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
|
||||||
F ext/fiddle/sqlite3-api.js 5f256e3dc78ed0ac4f8556c0c77860812f9baf542b7a73b19b2abb72a6e13146
|
F ext/fiddle/sqlite3-api.js ab7e7ded7b3079ee7de43e8290f1942e757d90ebb47ae4654cfe03c980cd0cad
|
||||||
F ext/fiddle/testing-common.js 37b014758db7e5e74278e37dc712ced2fc9b40d0617f5ed0b8b64a6bd9c0a45d
|
F ext/fiddle/testing-common.js 2b2826a1e7c8ca3e610dfa4255ff1077438b6570e08096cc139c226811e60dbb
|
||||||
F ext/fiddle/testing1.html 68cec1b1c8646a071717e5979f22e4268e6d36d96ba13ad68333351acdbcf1d1
|
F ext/fiddle/testing1.html 68cec1b1c8646a071717e5979f22e4268e6d36d96ba13ad68333351acdbcf1d1
|
||||||
F ext/fiddle/testing1.js 2e9aa40a17c97ab8e90a8ba942725ebf590ae5db3f0329583d7431e4524f5b11
|
F ext/fiddle/testing1.js 6a314a10efc954bcd854af89d53ab768f48a42d3dcb80773b297f4ba0ac0236d
|
||||||
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
|
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
|
||||||
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
|
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
|
||||||
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
|
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
|
||||||
@ -1967,8 +1967,8 @@ 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 dea098b64eb95c395b346ebcae687afe42b7d21df48833527808c02226300a66
|
P 84c8f63a1c446331a3afe52b0c8bdfa6980f24aa4cf600f576877fef5e650c39
|
||||||
R d6777acddc9dd48a02f29eb36eccd673
|
R d3343607f5b0d8f2cee2f7485f266550
|
||||||
U stephan
|
U stephan
|
||||||
Z ec1032561dde0c5db030c66f88c029fe
|
Z 4ea03d4418e5522f96fc7a3cf30bf26a
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
84c8f63a1c446331a3afe52b0c8bdfa6980f24aa4cf600f576877fef5e650c39
|
f3bc0328c87cac7d50513b0f13576d8fe7b411396f19c08fbe7e7c657b33cfbf
|
Reference in New Issue
Block a user