xref: /sqlite-3.40.0/ext/wasm/demo-123.js (revision cd0df83c)
1/*
2  2022-09-19
3
4  The author disclaims copyright to this source code.  In place of a
5  legal notice, here is a blessing:
6
7  *   May you do good and not evil.
8  *   May you find forgiveness for yourself and forgive others.
9  *   May you share freely, never taking more than you give.
10
11  ***********************************************************************
12
13  A basic demonstration of the SQLite3 "OO#1" API.
14*/
15'use strict';
16(function(){
17  /**
18     Set up our output channel differently depending
19     on whether we are running in a worker thread or
20     the main (UI) thread.
21  */
22  let logHtml;
23  if(self.window === self /* UI thread */){
24    console.log("Running demo from main UI thread.");
25    logHtml = function(cssClass,...args){
26      const ln = document.createElement('div');
27      if(cssClass) ln.classList.add(cssClass);
28      ln.append(document.createTextNode(args.join(' ')));
29      document.body.append(ln);
30    };
31  }else{ /* Worker thread */
32    console.log("Running demo from Worker thread.");
33    logHtml = function(cssClass,...args){
34      postMessage({
35        type:'log',
36        payload:{cssClass, args}
37      });
38    };
39  }
40  const log = (...args)=>logHtml('',...args);
41  const warn = (...args)=>logHtml('warning',...args);
42  const error = (...args)=>logHtml('error',...args);
43
44  const demo1 = function(sqlite3){
45    const capi = sqlite3.capi/*C-style API*/,
46          oo = sqlite3.oo1/*high-level OO API*/;
47    log("sqlite3 version",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
48    const db = new oo.DB("/mydb.sqlite3",'ct');
49    log("transient db =",db.filename);
50    /**
51       Never(!) rely on garbage collection to clean up DBs and
52       (especially) prepared statements. Always wrap their lifetimes
53       in a try/finally construct, as demonstrated below. By and
54       large, client code can entirely avoid lifetime-related
55       complications of prepared statement objects by using the
56       DB.exec() method for SQL execution.
57    */
58    try {
59      log("Create a table...");
60      db.exec("CREATE TABLE IF NOT EXISTS t(a,b)");
61      //Equivalent:
62      db.exec({
63        sql:"CREATE TABLE IF NOT EXISTS t(a,b)"
64        // ... numerous other options ...
65      });
66      // SQL can be either a string or a byte array
67      // or an array of strings which get concatenated
68      // together as-is (so be sure to end each statement
69      // with a semicolon).
70
71      log("Insert some data using exec()...");
72      let i;
73      for( i = 20; i <= 25; ++i ){
74        db.exec({
75          sql: "insert into t(a,b) values (?,?)",
76          // bind by parameter index...
77          bind: [i, i*2]
78        });
79        db.exec({
80          sql: "insert into t(a,b) values ($a,$b)",
81          // bind by parameter name...
82          bind: {$a: i * 10, $b: i * 20}
83        });
84      }
85
86      log("Insert using a prepared statement...");
87      let q = db.prepare([
88        // SQL may be a string or array of strings
89        // (concatenated w/o separators).
90        "insert into t(a,b) ",
91        "values(?,?)"
92      ]);
93      try {
94        for( i = 100; i < 103; ++i ){
95          q.bind( [i, i*2] ).step();
96          q.reset();
97        }
98        // Equivalent...
99        for( i = 103; i <= 105; ++i ){
100          q.bind(1, i).bind(2, i*2).stepReset();
101        }
102      }finally{
103        q.finalize();
104      }
105
106      log("Query data with exec() using rowMode 'array'...");
107      db.exec({
108        sql: "select a from t order by a limit 3",
109        rowMode: 'array', // 'array' (default), 'object', or 'stmt'
110        callback: function(row){
111          log("row ",++this.counter,"=",row);
112        }.bind({counter: 0})
113      });
114
115      log("Query data with exec() using rowMode 'object'...");
116      db.exec({
117        sql: "select a as aa, b as bb from t order by aa limit 3",
118        rowMode: 'object',
119        callback: function(row){
120          log("row ",++this.counter,"=",JSON.stringify(row));
121        }.bind({counter: 0})
122      });
123
124      log("Query data with exec() using rowMode 'stmt'...");
125      db.exec({
126        sql: "select a from t order by a limit 3",
127        rowMode: 'stmt',
128        callback: function(row){
129          log("row ",++this.counter,"get(0) =",row.get(0));
130        }.bind({counter: 0})
131      });
132
133      log("Query data with exec() using rowMode INTEGER (result column index)...");
134      db.exec({
135        sql: "select a, b from t order by a limit 3",
136        rowMode: 1, // === result column 1
137        callback: function(row){
138          log("row ",++this.counter,"b =",row);
139        }.bind({counter: 0})
140      });
141
142      log("Query data with exec() using rowMode $COLNAME (result column name)...");
143      db.exec({
144        sql: "select a a, b from t order by a limit 3",
145        rowMode: '$a',
146        callback: function(value){
147          log("row ",++this.counter,"a =",value);
148        }.bind({counter: 0})
149      });
150
151      log("Query data with exec() without a callback...");
152      let resultRows = [];
153      db.exec({
154        sql: "select a, b from t order by a limit 3",
155        rowMode: 'object',
156        resultRows: resultRows
157      });
158      log("Result rows:",JSON.stringify(resultRows,undefined,2));
159
160      log("Create a scalar UDF...");
161      db.createFunction({
162        name: 'twice',
163        xFunc: function(pCx, arg){ // note the call arg count
164          return arg + arg;
165        }
166      });
167      log("Run scalar UDF and collect result column names...");
168      let columnNames = [];
169      db.exec({
170        sql: "select a, twice(a), twice(''||a) from t order by a desc limit 3",
171        columnNames: columnNames,
172        rowMode: 'stmt',
173        callback: function(row){
174          log("a =",row.get(0), "twice(a) =", row.get(1),
175              "twice(''||a) =",row.get(2));
176        }
177      });
178      log("Result column names:",columnNames);
179
180      try{
181        log("The following use of the twice() UDF will",
182            "fail because of incorrect arg count...");
183        db.exec("select twice(1,2,3)");
184      }catch(e){
185        warn("Got expected exception:",e.message);
186      }
187
188      try {
189        db.transaction( function(D) {
190          D.exec("delete from t");
191          log("In transaction: count(*) from t =",db.selectValue("select count(*) from t"));
192          throw new sqlite3.SQLite3Error("Demonstrating transaction() rollback");
193        });
194      }catch(e){
195        if(e instanceof sqlite3.SQLite3Error){
196          log("Got expected exception from db.transaction():",e.message);
197          log("count(*) from t =",db.selectValue("select count(*) from t"));
198        }else{
199          throw e;
200        }
201      }
202
203      try {
204        db.savepoint( function(D) {
205          D.exec("delete from t");
206          log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t"));
207          D.savepoint(function(DD){
208            const rows = [];
209            DD.exec({
210              sql: ["insert into t(a,b) values(99,100);",
211                    "select count(*) from t"],
212              rowMode: 0,
213              resultRows: rows
214            });
215            log("In nested savepoint. Row count =",rows[0]);
216            throw new sqlite3.SQLite3Error("Demonstrating nested savepoint() rollback");
217          })
218        });
219      }catch(e){
220        if(e instanceof sqlite3.SQLite3Error){
221          log("Got expected exception from nested db.savepoint():",e.message);
222          log("count(*) from t =",db.selectValue("select count(*) from t"));
223        }else{
224          throw e;
225        }
226      }
227    }finally{
228      db.close();
229    }
230
231    log("That's all, folks!");
232
233    /**
234       Some of the features of the OO API not demonstrated above...
235
236       - get change count (total or statement-local, 32- or 64-bit)
237       - get a DB's file name
238
239       Misc. Stmt features:
240
241       - Various forms of bind()
242       - clearBindings()
243       - reset()
244       - Various forms of step()
245       - Variants of get() for explicit type treatment/conversion,
246         e.g. getInt(), getFloat(), getBlob(), getJSON()
247       - getColumnName(ndx), getColumnNames()
248       - getParamIndex(name)
249    */
250  }/*demo1()*/;
251
252  log("Loading and initializing sqlite3 module...");
253  if(self.window!==self) /*worker thread*/{
254    /*
255      If sqlite3.js is in a directory other than this script, in order
256      to get sqlite3.js to resolve sqlite3.wasm properly, we have to
257      explicitly tell it where sqlite3.js is being loaded from. We do
258      that by passing the `sqlite3.dir=theDirName` URL argument to
259      _this_ script. That URL argument will be seen by the JS/WASM
260      loader and it will adjust the sqlite3.wasm path accordingly. If
261      sqlite3.js/.wasm are in the same directory as this script then
262      that's not needed.
263
264      URL arguments passed as part of the filename via importScripts()
265      are simply lost, and such scripts see the self.location of
266      _this_ script.
267    */
268    let sqlite3Js = 'sqlite3.js';
269    const urlParams = new URL(self.location.href).searchParams;
270    if(urlParams.has('sqlite3.dir')){
271      sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
272    }
273    importScripts(sqlite3Js);
274  }
275  self.sqlite3InitModule({
276    // We can redirect any stdout/stderr from the module
277    // like so...
278    print: log,
279    printErr: error
280  }).then(function(sqlite3){
281    //console.log('sqlite3 =',sqlite3);
282    log("Done initializing. Running demo...");
283    try {
284      demo1(sqlite3);
285    }catch(e){
286      error("Exception:",e.message);
287    }
288  });
289})();
290