]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
wasm: add a small demo/presentation app for JS OO API #1 and make a few minor additio...
authorstephan <stephan@noemail.net>
Tue, 16 Aug 2022 16:36:19 +0000 (16:36 +0000)
committerstephan <stephan@noemail.net>
Tue, 16 Aug 2022 16:36:19 +0000 (16:36 +0000)
FossilOrigin-Name: d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483

ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/demo-oo1.html [new file with mode: 0644]
ext/wasm/demo-oo1.js [new file with mode: 0644]
ext/wasm/testing1.html
manifest
manifest.uuid

index b6e63d540a60728e4b3d05b81a9559e157c8e278..e54e27ec2bd5a8657c50348467f161d972b13c21 100644 (file)
      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));
        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
        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
        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
         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;
             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
        ```
     */
     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
           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 (file)
index 0000000..9b6e8cb
--- /dev/null
@@ -0,0 +1,34 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
+    <link rel="stylesheet" href="common/emscripten.css"/>
+    <link rel="stylesheet" href="common/testing.css"/>
+    <title>sqlite3-api OO #1 Demo</title>
+  </head>
+  <body>
+    <header id='titlebar'><span>sqlite3-api OO #1 Demo</span></header>
+    <!-- emscripten bits -->
+    <figure id="module-spinner">
+      <div class="spinner"></div>
+      <div class='center'><strong>Initializing app...</strong></div>
+      <div class='center'>
+        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.
+      </div>
+    </figure>
+    <div class="emscripten" id="module-status">Downloading...</div>
+    <div class="emscripten">
+      <progress value="0" max="100" id="module-progress" hidden='1'></progress>  
+    </div><!-- /emscripten bits -->
+    <div>Most stuff on this page happens in the dev console.</div>
+    <hr>
+    <div id='test-output'></div>
+    <script src="sqlite3.js"></script>
+    <script src="common/SqliteTestUtil.js"></script>
+    <script src="demo-oo1.js"></script>
+  </body>
+</html>
diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js
new file mode 100644 (file)
index 0000000..2792b50
--- /dev/null
@@ -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);
+})();
index 0c644702216bcd18b953af0769d11ff9eb3d4903..24c1e8236056b16e325e925cec31939c859f7367 100644 (file)
@@ -27,7 +27,7 @@
     <div>Most stuff on this page happens in the dev console.</div>
     <hr>
     <div id='test-output'></div>
-    <script src="api/sqlite3.js"></script>
+    <script src="sqlite3.js"></script>
     <script src="common/SqliteTestUtil.js"></script>
     <script src="testing1.js"></script>
   </body>
index c19c285c0d2ce56a3f19a650d90869417490b732..df72b2cc8a7b55ce0ae77d8cd94b1a9f15958d48 100644 (file)
--- 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.
index 3e6aa5a962e6bcdf41a09ffccb74139ddcc64cb7..bbadb22b441409fd54360f225a2ccfd7b9603d81 100644 (file)
@@ -1 +1 @@
-6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14
\ No newline at end of file
+d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483
\ No newline at end of file