diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index b6e63d540a..e54e27ec2b 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -80,35 +80,57 @@ not resolve to real filenames, but "" uses an on-storage temporary database and requires that the VFS support that. - The db is currently opened with a fixed set of flags: - (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXRESCODE). This API will change in the future - permit the caller to provide those flags via an additional - argument. + The second argument specifies the open/create mode for the + database. It must be string containing a sequence of letters (in + any order, but case sensitive) specifying the mode: + + - "c" => create if it does not exist, else fail if it does not + exist. Implies the "w" flag. + + - "w" => write. Implies "r": a db cannot be write-only. + + - "r" => read-only if neither "w" nor "c" are provided, else it + is ignored. + + If "w" is not provided, the db is implicitly read-only, noting that + "rc" is meaningless + + Any other letters are currently ignored. The default is + "c". These modes are ignored for the special ":memory:" and "" + names. + + The final argument is currently unimplemented but will eventually + be used to specify an optional sqlite3 VFS implementation name, + as for the final argument to sqlite3_open_v2(). For purposes of passing a DB instance to C-style sqlite3 - functions, its read-only `pointer` property holds its `sqlite3*` - pointer value. That property can also be used to check whether - this DB instance is still open. + functions, the DB object's read-only `pointer` property holds its + `sqlite3*` pointer value. That property can also be used to check + whether this DB instance is still open. */ - const DB = function ctor(fn=':memory:'){ + const DB = function ctor(fn=':memory:', flags='c', vtab="not yet implemented"){ if('string'!==typeof fn){ toss3("Invalid filename for DB constructor."); } + let ptr, oflags = 0; + if( flags.indexOf('c')>=0 ){ + oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + } + if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; + if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; + oflags |= capi.SQLITE_OPEN_EXRESCODE; const stack = capi.wasm.scopedAllocPush(); - let ptr; try { const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; - const rc = capi.sqlite3_open_v2(fn, ppDb, capi.SQLITE_OPEN_READWRITE - | capi.SQLITE_OPEN_CREATE - | capi.SQLITE_OPEN_EXRESCODE, null); + const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, null); ptr = capi.wasm.getMemValue(ppDb, '*'); ctor.checkRc(ptr, rc); - }catch(e){ - if(ptr) capi.sqlite3_close_v2(ptr); + }catch( e ){ + if( ptr ) capi.sqlite3_close_v2(ptr); throw e; + }finally{ + capi.wasm.scopedAllocPop(stack); } - finally{capi.wasm.scopedAllocPop(stack);} this.filename = fn; __ptrMap.set(this, ptr); __stmtMap.set(this, Object.create(null)); @@ -315,11 +337,10 @@ file. If the name is "" or ":memory:", it resolves to false. Note that it is not aware of the peculiarities of URI-style names and a URI-style name for a ":memory:" db will fool it. + Returns false if this db is closed. */ hasFilename: function(){ - const fn = this.filename; - if(!fn || ':memory'===fn) return false; - return true; + return this.filename && ':memory'!==this.filename; }, /** Returns the name of the given 0-based db number, as documented @@ -451,14 +472,14 @@ statement in the SQL which has any bindable parameters. (Empty statements are skipped entirely.) - - .callback = a function which gets called for each row of - the FIRST statement in the SQL which has result - _columns_, but only if that statement has any result - _rows_. The second argument passed to the callback is - always the current Stmt object (so that the caller may - collect column names, or similar). The first argument - passed to the callback defaults to the current Stmt - object but may be changed with ... + - .callback = a function which gets called for each row of the + FIRST statement in the SQL which has result _columns_, but only + if that statement has any result _rows_. The callback's "this" + is the options object. The second argument passed to the + callback is always the current Stmt object (so that the caller + may collect column names, or similar). The first argument + passed to the callback defaults to the current Stmt object but + may be changed with ... - .rowMode = either a string describing what type of argument should be passed as the first argument to the callback or an @@ -479,12 +500,13 @@ the FIRST first statement which has result _columns_ is appended to the array in the format specified for the `rowMode` option, with the exception that the only legal values for - `rowMode` in this case are 'array' or 'object', neither of - which is the default. It is legal to use both `resultRows` and - `callback`, but `resultRows` is likely much simpler to use for - small data sets and can be used over a WebWorker-style message - interface. execMulti() throws if `resultRows` is set and - `rowMode` is 'stmt' (which is the default!). + `rowMode` in this case are 'array', 'object', or an integer, + none of which are the default for `rowMode`. It is legal to use + both `resultRows` and `callback`, but `resultRows` is likely + much simpler to use for small data sets and can be used over a + WebWorker-style message interface. execMulti() throws if + `resultRows` is set and `rowMode` is 'stmt' (which is the + default!). - saveSql = an optional array. If set, the SQL of each executed statement is appended to this array before the @@ -1081,6 +1103,7 @@ delete __stmtMap.get(this.db)[this.pointer]; capi.sqlite3_finalize(this.pointer); __ptrMap.delete(this); + delete this._mayGet; delete this.columnCount; delete this.parameterCount; delete this.db; @@ -1268,6 +1291,27 @@ DB.checkRc(this.db.pointer, rc); } }, + /** + Functions exactly like step() except that... + + 1) On success, it calls this.reset() and returns this object. + 2) On error, it throws and does not call reset(). + + This is intended to simplify constructs like: + + ``` + for(...) { + stmt.bind(...).stepReset(); + } + ``` + + Note that the reset() call makes it illegal to call this.get() + after the step. + */ + stepReset: function(){ + this.step(); + return this.reset(); + }, /** Functions like step() except that it finalizes this statement immediately after stepping unless @@ -1286,17 +1330,9 @@ ``` */ stepFinalize: function(){ - affirmUnlocked(this, 'step()'); - const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); - switch(rc){ - case capi.SQLITE_DONE: this.finalize(); return false; - case capi.SQLITE_ROW: this.finalize(); return true; - default: - this.finalize(); - console.warn("sqlite3_step() rc=",rc,"SQL =", - capi.sqlite3_sql(this.pointer)); - DB.checkRc(this.db.pointer, rc); - } + const rc = this.step(); + this.finalize(); + return rc; }, /** Fetches the value from the given 0-based column index of @@ -1400,7 +1436,7 @@ default: toss3("Don't know how to translate", "type of result column #"+ndx+"."); } - abort("Not reached."); + toss3("Not reached."); }, /** Equivalent to get(ndx) but coerces the result to an integer. */ diff --git a/ext/wasm/demo-oo1.html b/ext/wasm/demo-oo1.html new file mode 100644 index 0000000000..9b6e8cbfa3 --- /dev/null +++ b/ext/wasm/demo-oo1.html @@ -0,0 +1,34 @@ + + + + + + + + + sqlite3-api OO #1 Demo + + +
sqlite3-api OO #1 Demo
+ +
+
+
Initializing app...
+
+ On a slow internet connection this may take a moment. If this + message displays for "a long time", intialization may have + failed and the JavaScript console may contain clues as to why. +
+
+
Downloading...
+
+ +
+
Most stuff on this page happens in the dev console.
+
+
+ + + + + diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js new file mode 100644 index 0000000000..2792b50920 --- /dev/null +++ b/ext/wasm/demo-oo1.js @@ -0,0 +1,218 @@ +/* + 2022-08-16 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + A basic demonstration of the SQLite3 OO API #1, shorn of assertions + and the like to improve readability. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const toss = function(...args){throw new Error(args.join(' '))}; + const debug = console.debug.bind(console), + log = console.log.bind(console), + warn = console.warn.bind(console), + error = console.error.bind(console); + + const demo1 = function(sqlite3,EmModule){ + const capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + + // If we have persistent storage, maybe init and mount it: + const dbDir = true + ? "" // this demo works better without persistent storage. + : capi.sqlite3_web_persistent_dir(); + // ^^^ returns name of persistent mount point or "" if we have none + + const db = new oo.DB(dbDir+"/mydb.sqlite3"); + /** + Never(!) rely on garbage collection to clean up DBs and + (especially) statements. Always wrap their lifetimes in + try/finally construct... + */ + try { + log("Create a table..."); + db.exec("CREATE TABLE IF NOT EXISTS t(a,b)"); + //Equivalent: + db.exec({ + sql:"CREATE TABLE IF NOT EXISTS t(a,b)" + // ... numerous other options ... + }); + // SQL can be either a string or a byte array + + log("Insert some data using exec()..."); + let i; + for( i = 1; i <= 5; ++i ){ + db.exec({ + sql: "insert into t(a,b) values (?,?)", + // bind by parameter index... + bind: [i, i*2] + }); + db.exec({ + sql: "insert into t(a,b) values ($a,$b)", + // bind by parameter name... + bind: {$a: i * 3, $b: i * 4} + }); + } + + log("Insert using a prepared statement..."); + let q = db.prepare("insert into t(a,b) values(?,?)"); + try { + for( i = 100; i < 103; ++i ){ + q.bind( [i, i*2] ).step(); + q.reset(); + } + // Equivalent... + for( i = 103; i <= 105; ++i ){ + q.bind(1, i).bind(2, i*2).stepReset(); + } + }finally{ + q.finalize(); + } + + log("Query data with exec() using rowMode 'array'..."); + db.exec({ + sql: "select a from t order by a limit 3", + rowMode: 'array', // 'array', 'object', or 'stmt' (default) + callback: function(row){ + log("row ",++this.counter,"=",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode 'object'..."); + db.exec({ + sql: "select a as aa, b as bb from t order by aa limit 3", + rowMode: 'object', + callback: function(row){ + log("row ",++this.counter,"=",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode 'stmt'..."); + db.exec({ + sql: "select a from t order by a limit 3", + rowMode: 'stmt', // stmt === the default + callback: function(row){ + log("row ",++this.counter,"get(0) =",row.get(0)); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode INTEGER (result column index)..."); + db.exec({ + sql: "select a, b from t order by a limit 3", + rowMode: 1, // === result column 1 + callback: function(row){ + log("row ",++this.counter,"b =",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() without a callback..."); + let resultRows = []; + db.exec({ + sql: "select a, b from t order by a limit 3", + rowMode: 'object', + resultRows: resultRows + }); + log("Result rows:",resultRows); + + log("Create a scalar UDF..."); + db.createFunction({ + name: 'twice', + callback: function(arg){ // note the call arg count + return arg + arg; + } + }); + log("Run scalar UDF and collect result column names..."); + let columnNames = []; + db.exec({ + sql: "select a, twice(a), twice(''||a) from t order by a desc limit 3", + columnNames: columnNames, + callback: function(row){ + log("a =",row.get(0), "twice(a) =", row.get(1), "twice(''||a) =",row.get(2)); + } + }); + log("Result column names:",columnNames); + + /** + Main differences between exec() and execMulti(): + + - execMulti() traverses all statements in the input SQL + + - exec() supports a couple options not supported by execMulti(), + and vice versa. + + - execMulti() result callback/array only activates for the + first statement which has result columns. It is arguable + whether it should support a callback at all, and that + feature may be removed. + + - execMulti() column-bind data only activates for the first statement + with bindable columns. This feature is arguable and may be removed. + */ + + if(0){ + warn("UDF will throw because of incorrect arg count..."); + db.exec("select twice(1,2,3)"); + } + + try { + db.callInTransaction( function(D) { + D.exec("delete from t"); + log("In transaction: count(*) from t =",db.selectValue("select count(*) from t")); + throw new sqlite3.SQLite3Error("Demonstrating callInTransaction() rollback"); + }); + }catch(e){ + log("Got expected exception:",e.message); + log("count(*) from t =",db.selectValue("select count(*) from t")); + } + + }finally{ + db.close(); + } + + /** + Misc. DB features: + + - get change count (total or statement-local, 32- or 64-bit) + - get its file name + - selectValue() takes SQL and returns first column of first row. + + Misc. Stmt features: + + - Various forms of bind() + - clearBindings() + - reset() + - Various forms of step() + - Variants of get() for explicit type treatment/conversion, + e.g. getInt(), getFloat(), getBlob(), getJSON() + - getColumnName(ndx), getColumnNames() + - getParamIndex(name) + */ + }/*demo1()*/; + + const runDemos = function(Module){ + //log("Module",Module); + const sqlite3 = Module.sqlite3, + capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + try { + [ demo1 ].forEach((f)=>f(sqlite3, Module)) + }catch(e){ + error("Exception:",e.message); + throw e; + } + }; + + sqlite3InitModule(self.sqlite3TestModule).then(runDemos); +})(); diff --git a/ext/wasm/testing1.html b/ext/wasm/testing1.html index 0c64470221..24c1e82360 100644 --- a/ext/wasm/testing1.html +++ b/ext/wasm/testing1.html @@ -27,7 +27,7 @@
Most stuff on this page happens in the dev console.

- + diff --git a/manifest b/manifest index c19c285c0d..df72b2cc8a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\smove\sanother\sfile\sand\supdate\stesting1/testing2\sto\saccount\sfor\s[e38d00c2b82d].\sDisable\swasmfs\sby\sdefault\sas\sit\sbreaks\sthe\sworker-based\smodule\sloader\s(reason\sas\syet\sunknown). -D 2022-08-16T16:16:25.326 +C wasm:\sadd\sa\ssmall\sdemo/presentation\sapp\sfor\sJS\sOO\sAPI\s#1\sand\smake\sa\sfew\sminor\sadditions\sto\sthat\sAPI. +D 2022-08-16T16:36:19.533 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b -F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 +F ext/wasm/api/sqlite3-api-oo1.js 1d63e7e453e38ff2ad0c5e8bf68345f6fc5fe99fbc4a893cc982b4c50d904ca0 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 F ext/wasm/api/sqlite3-api-prologue.js c0f335bf8b44071da0204b8fa95ce78fd737033b155e7bcfdaee6ae64600802f F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd @@ -493,6 +493,8 @@ F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae +F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa +F ext/wasm/demo-oo1.js 0b1f85ee622b8f0ffe133ed88584bfc6b1ef1dcbe5b605278073e4694ebd0a2f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -506,8 +508,8 @@ F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 -F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f w ext/wasm/api/sqlite3-worker.js -F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 +F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f +F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 F ext/wasm/testing2.js dbb825174878716fab42c340962c0c1b32bfbe26dd16c7b082d30d35f510466c @@ -2004,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 -R 8ceb5e9180bc147ed0af56485cecdca6 +P 6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 +R 21ffe0bceeaf14b3e3d3b64b375f9eb8 U stephan -Z 7a0a645037fbcb33d5a75b781a0d284d +Z cfe198e466da02226e0ffe831d5c4056 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3e6aa5a962..bbadb22b44 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 \ No newline at end of file +d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 \ No newline at end of file