_sqlite3_bind_blob
_sqlite3_bind_double
_sqlite3_bind_int
+_sqlite3_bind_int64
+_sqlite3_bind_null
_sqlite3_bind_parameter_index
_sqlite3_bind_text
_sqlite3_changes
_sqlite3_column_type
_sqlite3_create_function_v2
_sqlite3_data_count
+_sqlite3_db_filename
_sqlite3_errmsg
_sqlite3_exec
_sqlite3_finalize
+_sqlite3_interrupt
_sqlite3_libversion
_sqlite3_open
_sqlite3_prepare_v2
SQLITE_FLOAT: 2,
SQLITE_TEXT: 3,
SQLITE_BLOB: 4,
+ SQLITE_NULL: 5,
/* sqlite encodings, used for creating UDFs, noting that we
will only support UTF8. */
SQLITE_UTF8: 1
use for the JS-side binding. That's required when overloading
a binding for two different uses.
*/
- ["sqlite3_open", "number", ["string", "number"]],
- ["sqlite3_close_v2", "number", ["number"]],
- ["sqlite3_exec", "number",
- ["number", "string", "number", "number", "number"]],
+ ["sqlite3_bind_blob","number",["number", "number", "number", "number", "number"]],
+ ["sqlite3_bind_double","number",["number", "number", "number"]],
+ ["sqlite3_bind_int","number",["number", "number", "number"]],
+ ["sqlite3_bind_int64","number",["number", "number", "number"]],
+ ["sqlite3_bind_null","void",["number"]],
+ ["sqlite3_bind_parameter_index","number",["number", "string"]],
+ ["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]],
["sqlite3_changes", "number", ["number"]],
+ ["sqlite3_clear_bindings","number",["number"]],
+ ["sqlite3_close_v2", "number", ["number"]],
+ ["sqlite3_column_blob","number", ["number", "number"]],
+ ["sqlite3_column_bytes","number",["number", "number"]],
+ ["sqlite3_column_count", "number", ["number"]],
+ ["sqlite3_column_count","number",["number"]],
+ ["sqlite3_column_double","number",["number", "number"]],
+ ["sqlite3_column_name","string",["number", "number"]],
+ ["sqlite3_column_text","string",["number", "number"]],
+ ["sqlite3_column_type","number",["number", "number"]],
+ ["sqlite3_create_function_v2", "number",
+ ["number", "string", "number", "number","number",
+ "number", "number", "number", "number"]],
+ ["sqlite3_data_count", "number", ["number"]],
+ ["sqlite3_db_filename", "string", ["number", "string"]],
+ ["sqlite3_errmsg", "string", ["number"]],
+ ["sqlite3_exec", "number", ["number", "string", "number", "number", "number"]],
+ ["sqlite3_finalize", "number", ["number"]],
+ ["sqlite3_interrupt", "void", ["number"]],
+ ["sqlite3_libversion", "string", []],
+ ["sqlite3_open", "number", ["string", "number"]],
["sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"]],
- ["sqlite3_prepare_v2_sqlptr",
+ ["sqlite3_prepare_v2_sqlptr", "sqlite3_prepare_v2",
/* Impl which requires that the 2nd argument be a pointer to
the SQL, instead of a string. This is used for cases where
we require a non-NULL value for the final argument. We may
or may not need this, depending on how our higher-level
API shapes up, but this code's spiritual guide (sql.js)
uses it we we'll include it. */
- "sqlite3_prepare_v2",
"number", ["number", "number", "number", "number", "number"]],
- ["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]],
- ["sqlite3_bind_blob","number",["number", "number", "number", "number", "number"]],
- ["sqlite3_bind_double","number",["number", "number", "number"]],
- ["sqlite3_bind_int","number",["number", "number", "number"]],
- ["sqlite3_bind_parameter_index","number",["number", "string"]],
- ["sqlite3_step", "number", ["number"]],
- ["sqlite3_errmsg", "string", ["number"]],
- ["sqlite3_column_count","number",["number"]],
- ["sqlite3_data_count", "number", ["number"]],
- ["sqlite3_column_count", "number", ["number"]],
- ["sqlite3_column_double","number",["number", "number"]],
- ["sqlite3_column_text","string",["number", "number"]],
- ["sqlite3_column_blob","number", ["number", "number"]],
- ["sqlite3_column_bytes","number",["number", "number"]],
- ["sqlite3_column_type","number",["number", "number"]],
- ["sqlite3_column_name","string",["number", "number"]],
["sqlite3_reset", "number", ["number"]],
- ["sqlite3_clear_bindings","number",["number"]],
- ["sqlite3_finalize", "number", ["number"]],
- ["sqlite3_create_function_v2", "number",
- ["number", "string", "number", "number",
- "number", "number", "number", "number",
- "number"]],
- ["sqlite3_value_type", "number", ["number"]],
- ["sqlite3_value_bytes","number",["number"]],
- ["sqlite3_value_text", "string", ["number"]],
- ["sqlite3_value_blob", "number", ["number"]],
- ["sqlite3_value_double","number",["number"]],
+ ["sqlite3_result_blob",null,["number", "number", "number", "number"]],
["sqlite3_result_double",null,["number", "number"]],
+ ["sqlite3_result_error",null,["number", "string", "number"]],
+ ["sqlite3_result_int",null,["number", "number"]],
["sqlite3_result_null",null,["number"]],
["sqlite3_result_text",null,["number", "string", "number", "number"]],
- ["sqlite3_result_blob",null,["number", "number", "number", "number"]],
- ["sqlite3_result_int",null,["number", "number"]],
- ["sqlite3_result_error",null,["number", "string", "number"]],
- ["sqlite3_libversion", "string", []],
- ["sqlite3_sourceid", "string", []]
+ ["sqlite3_sourceid", "string", []],
+ ["sqlite3_step", "number", ["number"]],
+ ["sqlite3_value_blob", "number", ["number"]],
+ ["sqlite3_value_bytes","number",["number"]],
+ ["sqlite3_value_double","number",["number"]],
+ ["sqlite3_value_text", "string", ["number"]],
+ ["sqlite3_value_type", "number", ["number"]]
//["sqlite3_sql", "string", ["number"]],
//["sqlite3_normalized_sql", "string", ["number"]]
- ].forEach(function(e){
- const a = Array.prototype.slice.call(e);
+ ].forEach(function(a){
const k = (4==a.length) ? a.shift() : a[0];
api[k] = cwrap.apply(this, a);
});
A basic test script for sqlite3-api.js.
*/
-(function(){
- self.Module.onRuntimeInitialized = function(){
- console.log("Loading sqlite3-api.js...");
- self.Module.loadSqliteAPI(function(S){
- console.log("Loaded module:",S.sqlite3_libversion(),
- S.sqlite3_sourceid());
- });
- };
-})(self/*window or worker*/);
+const setupAPI = function(S/*sqlite3 module*/){
+
+ /* memory for use in some pointer-passing routines */
+ const pPtrArg = stackAlloc(4);
+ const dummyArg = {/*for restricting Stmt constructor to internal use*/};
+ const toss = function(){
+ 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(),
+ S.sqlite3_sourceid());
+ const oo = setupAPI(S);
+
+ const db = new oo.DB();
+ console.log("DB:",db.filename);
+};
+
+self/*window or worker*/.Module.onRuntimeInitialized = function(){
+ console.log("Loading sqlite3-api.js...");
+ self.Module.loadSqliteAPI(mainTest1);
+};
-C Build\srefactoring\sfor\sthe\sfiddle/wasm\sbits.\sSet\sup\swasm\sbinding\sof\sa\schunk\sof\sthe\score\sC\sAPI\sand\sadded\ssome\sinfastructure\sfor\screating\stest\spages\sfor\sit.
-D 2022-05-22T00:27:19.296
+C WASM\sOO\swrapper\s#1:\sprepare()\sand\sbind()\sAPIs\sare\sin\splace\sbut\sare\suntested,\spending\sfetch/get\sAPIs.
+D 2022-05-22T14:07:44.577
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0ff5d9cdfac204
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
-F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f w ext/fiddle/EXPORTED_FUNCTIONS
-F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 4b06e6c3ce8c8389274079ffb6b441ffff1a55e32a448cf21ce1da45a16c8a01
+F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f
+F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 a3a2862941270ae5e2633d21cbf44979901c4b75efa42a452c15ef879b47ad2b
F ext/fiddle/EXPORTED_RUNTIME_METHODS 91d5dcb0168ee056fa1a340cb8ab3c23d922622f8dad39d28919dd8af2b3ade0
F ext/fiddle/Makefile 9277c73e208b9c8093659256c9f07409c877e366480c7c22ec545ee345451d95
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c
F ext/fiddle/fiddle.js f9c79164428e96a5909532f18a8bc8f8c8ec4f738bfc09ad3d2a532c2400f9f0
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
-F ext/fiddle/sqlite3-api.js d3c6da99850e146e50dc42039ac027e5d9b08b9f24eb22b31d1982c49930ee7c
+F ext/fiddle/sqlite3-api.js 5f256e3dc78ed0ac4f8556c0c77860812f9baf542b7a73b19b2abb72a6e13146
F ext/fiddle/testing-common.js 37b014758db7e5e74278e37dc712ced2fc9b40d0617f5ed0b8b64a6bd9c0a45d
F ext/fiddle/testing1.html 68cec1b1c8646a071717e5979f22e4268e6d36d96ba13ad68333351acdbcf1d1
-F ext/fiddle/testing1.js 0fb900c768b06c2ec3922ab522f721a68b0756d200e3c66602461f45910bcd39
+F ext/fiddle/testing1.js 2e9aa40a17c97ab8e90a8ba942725ebf590ae5db3f0329583d7431e4524f5b11
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P c7cfdd4c3682659352642461d3307bf8180703b121ec1802ba5881f8e1ef9809
-R d89e623e1614875df0e3a579152a2be9
+P dea098b64eb95c395b346ebcae687afe42b7d21df48833527808c02226300a66
+R d6777acddc9dd48a02f29eb36eccd673
U stephan
-Z fd89698a5fe01334529b2fb407d595a1
+Z ec1032561dde0c5db030c66f88c029fe
# Remove this line to create a well-formed Fossil manifest.
-dea098b64eb95c395b346ebcae687afe42b7d21df48833527808c02226300a66
\ No newline at end of file
+84c8f63a1c446331a3afe52b0c8bdfa6980f24aa4cf600f576877fef5e650c39
\ No newline at end of file