SQLite

Check-in [2ab065133b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add configurable cache_size to batch-runner and speedtest1-worker. Add SQL tracing to demo-123.js just for demonstration's sake.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | fiddle-opfs
Files: files | file ages | folders
SHA3-256: 2ab065133bec19f71a1dc382946fdbd020920b2e38a526f4126a3a1e8df5de87
User & Date: stephan 2022-10-03 18:07:25.896
Context
2022-10-03
22:38
Minor JS doc updates and typo fixes. (check-in: 3cfcc14dfd user: stephan tags: fiddle-opfs)
18:07
Add configurable cache_size to batch-runner and speedtest1-worker. Add SQL tracing to demo-123.js just for demonstration's sake. (check-in: 2ab065133b user: stephan tags: fiddle-opfs)
18:05
Omit WAL mode from the wasm build. (check-in: 88efe2a62b user: stephan tags: fiddle-opfs)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/wasm/batch-runner.html.
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    </div>
    <hr>
      <span class='input-wrapper'>
        <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
        <label for='cb-reverse-log-order'>Reverse log order (newest first)</label>
      </span>
    <div id='test-output'></div>
    <!-- batch-runner.js "should" work with sqlite3-kvvfs so long as
         its data sets don't violate the the storage limits. -->
    <script src="sqlite3-wasmfs.js"></script>
    <script src="common/SqliteTestUtil.js"></script>
    <script src="batch-runner.js"></script>
    <style>
      .flex-col {
          display: flex;
          flex-direction: column;







<
<







65
66
67
68
69
70
71


72
73
74
75
76
77
78
    </div>
    <hr>
      <span class='input-wrapper'>
        <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
        <label for='cb-reverse-log-order'>Reverse log order (newest first)</label>
      </span>
    <div id='test-output'></div>


    <script src="sqlite3-wasmfs.js"></script>
    <script src="common/SqliteTestUtil.js"></script>
    <script src="batch-runner.js"></script>
    <style>
      .flex-col {
          display: flex;
          flex-direction: column;
Changes to ext/wasm/batch-runner.js.
14
15
16
17
18
19
20





21
22
23
24
25
26
27
  main JS thread and sqlite3.js must have been loaded before it.
*/
'use strict';
(function(){
  const toss = function(...args){throw new Error(args.join(' '))};
  const warn = console.warn.bind(console);
  let sqlite3;






  /** Throws if the given sqlite3 result code is not 0. */
  const checkSqliteRc = (dbh,rc)=>{
    if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh));
  };

  const sqlToDrop = [







>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  main JS thread and sqlite3.js must have been loaded before it.
*/
'use strict';
(function(){
  const toss = function(...args){throw new Error(args.join(' '))};
  const warn = console.warn.bind(console);
  let sqlite3;
  const urlParams = new URL(self.location.href).searchParams;
  const cacheSize = (()=>{
    if(urlParams.has('cachesize')) return +urlParams.get('cachesize');
    return 200;
  })();

  /** Throws if the given sqlite3 result code is not 0. */
  const checkSqliteRc = (dbh,rc)=>{
    if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh));
  };

  const sqlToDrop = [
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    });
  };

  const clearDbSqlite = function(db){
    // This would be SO much easier with the oo1 API, but we specifically want to
    // inject metrics we can't get via that API, and we cannot reliably (OPFS)
    // open the same DB twice to clear it using that API, so...
    let pStmt = 0, pSqlBegin;
    const capi = sqlite3.capi, wasm = capi.wasm;
    const scope = wasm.scopedAllocPush();
    try {
      const toDrop = [/* type, name pairs */];
      const ppStmt = wasm.scopedAllocPtr();
      // Collect list of tables/etc we can drop...
      let rc = capi.sqlite3_prepare_v2(db.handle, sqlToDrop, -1, ppStmt, null);
      checkSqliteRc(db.handle,rc);
      pStmt = wasm.getPtrValue(ppStmt);
      while(capi.SQLITE_ROW===capi.sqlite3_step(pStmt)){
        toDrop.push(capi.sqlite3_column_text(pStmt,0),
                    capi.sqlite3_column_text(pStmt,1));
      }
      capi.sqlite3_finalize(pStmt);
      pStmt = 0;
      // Build SQL to delete them...
      const sqlDrop = [];
      const doDrop = 0!==toDrop.length;
      while(doDrop){
        const name = toDrop.pop();
        if(name){
          const type = toDrop.pop();
          switch(type){
              case 'table': case 'view': case 'trigger': case 'index':
                sqlDrop.push('DROP '+type+' '+name);
                break;
              default:
                warn("Unhandled db entry type:",type,name);
                continue;
          }
        }else{
          sqlDrop.push("VACUUM");
          break;
        }
      }
      if(sqlDrop.length){
        const sqlClean = sqlDrop.join(';\n');
        console.log("Cleaning up",db.id,":",sqlClean);
        capi.sqlite3_exec(db.handle, sqlClean, 0, 0, 0);
      }
    }finally{
      if(pStmt) capi.sqlite3_finalize(pStmt);
      wasm.scopedAllocPop(scope);
    }
  };

  
  const E = (s)=>document.querySelector(s);
  const App = {
    e: {
      output: E('#test-output'),







<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







64
65
66
67
68
69
70

71



















72























73
74
75
76
77
78
79
    });
  };

  const clearDbSqlite = function(db){
    // This would be SO much easier with the oo1 API, but we specifically want to
    // inject metrics we can't get via that API, and we cannot reliably (OPFS)
    // open the same DB twice to clear it using that API, so...

    const rc = sqlite3.capi.wasm.exports.sqlite3_wasm_db_reset(db.handle);



















    App.logHtml("reset db rc =",rc,db.id, db.filename);























  };

  
  const E = (s)=>document.querySelector(s);
  const App = {
    e: {
      output: E('#test-output'),
517
518
519
520
521
522
523




524
525
526
527
528
529
530
531
532
533


534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
      }/*first-time init*/
      const dbId = this.e.selImpl.value;
      const d = this.dbs[dbId];
      if(d.handle) return d;
      if('websql' === dbId){
        d.handle = self.openDatabase('batch-runner', '0.1', 'foo', 1024 * 1024 * 50);
        d.clear = ()=>clearDbWebSQL(d);




      }else{
        const capi = this.sqlite3.capi, wasm = capi.wasm;
        const stack = wasm.scopedAllocPush();
        let pDb = 0;
        try{
          const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
          const ppDb = wasm.scopedAllocPtr();
          const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, null);
          pDb = wasm.getPtrValue(ppDb)
          if(rc) toss("sqlite3_open_v2() failed with code",rc);


        }catch(e){
          if(pDb) capi.sqlite3_close_v2(pDb);
        }finally{
          wasm.scopedAllocPop(stack);
        }
        d.handle = pDb;
        d.clear = ()=>clearDbSqlite(d);
      }
      d.clear();
      this.logHtml("Opened db:",dbId);
      console.log("db =",d);
      return d;
    },

    run: function(sqlite3){
      delete this.run;
      this.sqlite3 = sqlite3;







>
>
>
>










>
>









|







479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
      }/*first-time init*/
      const dbId = this.e.selImpl.value;
      const d = this.dbs[dbId];
      if(d.handle) return d;
      if('websql' === dbId){
        d.handle = self.openDatabase('batch-runner', '0.1', 'foo', 1024 * 1024 * 50);
        d.clear = ()=>clearDbWebSQL(d);
        d.handle.transaction(function(tx){
          tx.executeSql("PRAGMA cache_size="+cacheSize);
          App.logHtml(dbId,"cache_size =",cacheSize);
        });
      }else{
        const capi = this.sqlite3.capi, wasm = capi.wasm;
        const stack = wasm.scopedAllocPush();
        let pDb = 0;
        try{
          const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
          const ppDb = wasm.scopedAllocPtr();
          const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, null);
          pDb = wasm.getPtrValue(ppDb)
          if(rc) toss("sqlite3_open_v2() failed with code",rc);
          capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0);
          this.logHtml(dbId,"cache_size =",cacheSize);
        }catch(e){
          if(pDb) capi.sqlite3_close_v2(pDb);
        }finally{
          wasm.scopedAllocPop(stack);
        }
        d.handle = pDb;
        d.clear = ()=>clearDbSqlite(d);
      }
      d.clear();
      this.logHtml("Opened db:",dbId,d.filename);
      console.log("db =",d);
      return d;
    },

    run: function(sqlite3){
      delete this.run;
      this.sqlite3 = sqlite3;
Changes to ext/wasm/demo-123.js.
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  const warn = (...args)=>logHtml('warning',...args);
  const error = (...args)=>logHtml('error',...args);

  const demo1 = function(sqlite3){
    const capi = sqlite3.capi/*C-style API*/,
          oo = sqlite3.oo1/*high-level OO API*/;
    log("sqlite3 version",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
    const db = new oo.DB("/mydb.sqlite3");
    log("transient db =",db.filename);
    /**
       Never(!) rely on garbage collection to clean up DBs and
       (especially) prepared statements. Always wrap their lifetimes
       in a try/finally construct, as demonstrated below. By and
       large, client code can entirely avoid lifetime-related
       complications of prepared statement objects by using the







|







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  const warn = (...args)=>logHtml('warning',...args);
  const error = (...args)=>logHtml('error',...args);

  const demo1 = function(sqlite3){
    const capi = sqlite3.capi/*C-style API*/,
          oo = sqlite3.oo1/*high-level OO API*/;
    log("sqlite3 version",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
    const db = new oo.DB("/mydb.sqlite3",'ct');
    log("transient db =",db.filename);
    /**
       Never(!) rely on garbage collection to clean up DBs and
       (especially) prepared statements. Always wrap their lifetimes
       in a try/finally construct, as demonstrated below. By and
       large, client code can entirely avoid lifetime-related
       complications of prepared statement objects by using the
Changes to ext/wasm/speedtest1-worker.html.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    </div><!-- /emscripten bits -->
    <fieldset id='ui-controls' class='hidden'>
      <legend>Options</legend>
      <div id='toolbar'>
        <div id='toolbar-select'>
          <select id='select-flags' size='10' multiple></select>
          <div>The following flags can be passed as URL parameters:
            vfs=NAME, size=N, journal=MODE
          </div>
        </div>
        <div class='toolbar-inner-vertical'>
          <div id='toolbar-selected-flags'></div>
          <div class='toolbar-inner-vertical'>
            <span>&rarr; <a id='link-main-thread' href='#' target='speedtest-main'
                            title='Start speedtest1.html with the selected flags'>speedtest1.html</a>







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    </div><!-- /emscripten bits -->
    <fieldset id='ui-controls' class='hidden'>
      <legend>Options</legend>
      <div id='toolbar'>
        <div id='toolbar-select'>
          <select id='select-flags' size='10' multiple></select>
          <div>The following flags can be passed as URL parameters:
            vfs=NAME, size=N, journal=MODE, cachesize=BYTES
          </div>
        </div>
        <div class='toolbar-inner-vertical'>
          <div id='toolbar-selected-flags'></div>
          <div class='toolbar-inner-vertical'>
            <span>&rarr; <a id='link-main-thread' href='#' target='speedtest-main'
                            title='Start speedtest1.html with the selected flags'>speedtest1.html</a>
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    const eLinkMainThread = E('#link-main-thread');
    const eLinkWasmfs = E('#link-wasmfs');
    const eLinkKvvfs = E('#link-kvvfs');
    const urlParams = new URL(self.location.href).searchParams;
    const getSelectedFlags = ()=>{
        const f = Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value);
        [
            'size', 'vfs', 'journal'
        ].forEach(function(k){
            if(urlParams.has(k)) f.push('--'+k, urlParams.get(k));
        });
        return f;
    };
    const updateSelectedFlags = function(){
        eSelectedFlags.innerText = '';







|







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    const eLinkMainThread = E('#link-main-thread');
    const eLinkWasmfs = E('#link-wasmfs');
    const eLinkKvvfs = E('#link-kvvfs');
    const urlParams = new URL(self.location.href).searchParams;
    const getSelectedFlags = ()=>{
        const f = Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value);
        [
            'size', 'vfs', 'journal', 'cachesize'
        ].forEach(function(k){
            if(urlParams.has(k)) f.push('--'+k, urlParams.get(k));
        });
        return f;
    };
    const updateSelectedFlags = function(){
        eSelectedFlags.innerText = '';
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
    {
        const flags = Object.create(null);
        /* TODO? Flags which require values need custom UI
           controls and some of them make little sense here
           (e.g. --script FILE). */
        flags["--autovacuum"] = "Enable AUTOVACUUM mode";
        flags["--big-transactions"] = "Important for tests 410 and 510!";
        //flags["--cachesize"] = "N       Set the cache size to N";
        flags["--checkpoint"] = "Run PRAGMA wal_checkpoint after each test case";
        flags["--exclusive"] = "Enable locking_mode=EXCLUSIVE";
        flags["--explain"] = "Like --sqlonly but with added EXPLAIN keywords";
        //flags["--heap"] = "SZ MIN       Memory allocator uses SZ bytes & min allocation MIN";
        flags["--incrvacuum"] = "Enable incremenatal vacuum mode";
        //flags["--journal"] = "M         Set the journal_mode to M";
        //flags["--key"] = "KEY           Set the encryption key to KEY";







|







210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
    {
        const flags = Object.create(null);
        /* TODO? Flags which require values need custom UI
           controls and some of them make little sense here
           (e.g. --script FILE). */
        flags["--autovacuum"] = "Enable AUTOVACUUM mode";
        flags["--big-transactions"] = "Important for tests 410 and 510!";
        //flags["--cachesize"] = "N       Set the cache size to N pages";
        flags["--checkpoint"] = "Run PRAGMA wal_checkpoint after each test case";
        flags["--exclusive"] = "Enable locking_mode=EXCLUSIVE";
        flags["--explain"] = "Like --sqlonly but with added EXPLAIN keywords";
        //flags["--heap"] = "SZ MIN       Memory allocator uses SZ bytes & min allocation MIN";
        flags["--incrvacuum"] = "Enable incremenatal vacuum mode";
        //flags["--journal"] = "M         Set the journal_mode to M";
        //flags["--key"] = "KEY           Set the encryption key to KEY";