123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /*
- 2022-09-19
- 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#1" API.
- */
- 'use strict';
- (function(){
- /**
- Set up our output channel differently depending
- on whether we are running in a worker thread or
- the main (UI) thread.
- */
- let logHtml;
- if(globalThis.window === globalThis /* UI thread */){
- console.log("Running demo from main UI thread.");
- logHtml = function(cssClass,...args){
- const ln = document.createElement('div');
- if(cssClass) ln.classList.add(cssClass);
- ln.append(document.createTextNode(args.join(' ')));
- document.body.append(ln);
- };
- }else{ /* Worker thread */
- console.log("Running demo from Worker thread.");
- logHtml = function(cssClass,...args){
- postMessage({
- type:'log',
- payload:{cssClass, args}
- });
- };
- }
- const log = (...args)=>logHtml('',...args);
- 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
- DB.exec() method for SQL execution.
- */
- 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
- // or an array of strings which get concatenated
- // together as-is (so be sure to end each statement
- // with a semicolon).
- log("Insert some data using exec()...");
- let i;
- for( i = 20; i <= 25; ++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 * 10, $b: i * 20}
- });
- }
- log("Insert using a prepared statement...");
- let q = db.prepare([
- // SQL may be a string or array of strings
- // (concatenated w/o separators).
- "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' (default), 'object', or 'stmt'
- 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,"=",JSON.stringify(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',
- 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() using rowMode $COLNAME (result column name)...");
- db.exec({
- sql: "select a a, b from t order by a limit 3",
- rowMode: '$a',
- callback: function(value){
- log("row ",++this.counter,"a =",value);
- }.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:",JSON.stringify(resultRows,undefined,2));
- log("Create a scalar UDF...");
- db.createFunction({
- name: 'twice',
- xFunc: function(pCx, 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,
- rowMode: 'stmt',
- callback: function(row){
- log("a =",row.get(0), "twice(a) =", row.get(1),
- "twice(''||a) =",row.get(2));
- }
- });
- log("Result column names:",columnNames);
- try{
- log("The following use of the twice() UDF will",
- "fail because of incorrect arg count...");
- db.exec("select twice(1,2,3)");
- }catch(e){
- warn("Got expected exception:",e.message);
- }
- try {
- db.transaction( function(D) {
- D.exec("delete from t");
- log("In transaction: count(*) from t =",db.selectValue("select count(*) from t"));
- throw new sqlite3.SQLite3Error("Demonstrating transaction() rollback");
- });
- }catch(e){
- if(e instanceof sqlite3.SQLite3Error){
- log("Got expected exception from db.transaction():",e.message);
- log("count(*) from t =",db.selectValue("select count(*) from t"));
- }else{
- throw e;
- }
- }
- try {
- db.savepoint( function(D) {
- D.exec("delete from t");
- log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t"));
- D.savepoint(function(DD){
- const rows = [];
- DD.exec({
- sql: ["insert into t(a,b) values(99,100);",
- "select count(*) from t"],
- rowMode: 0,
- resultRows: rows
- });
- log("In nested savepoint. Row count =",rows[0]);
- throw new sqlite3.SQLite3Error("Demonstrating nested savepoint() rollback");
- })
- });
- }catch(e){
- if(e instanceof sqlite3.SQLite3Error){
- log("Got expected exception from nested db.savepoint():",e.message);
- log("count(*) from t =",db.selectValue("select count(*) from t"));
- }else{
- throw e;
- }
- }
- }finally{
- db.close();
- }
- log("That's all, folks!");
- /**
- Some of the features of the OO API not demonstrated above...
- - get change count (total or statement-local, 32- or 64-bit)
- - get a DB's file name
- 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()*/;
- log("Loading and initializing sqlite3 module...");
- if(globalThis.window!==globalThis) /*worker thread*/{
- /*
- If sqlite3.js is in a directory other than this script, in order
- to get sqlite3.js to resolve sqlite3.wasm properly, we have to
- explicitly tell it where sqlite3.js is being loaded from. We do
- that by passing the `sqlite3.dir=theDirName` URL argument to
- _this_ script. That URL argument will be seen by the JS/WASM
- loader and it will adjust the sqlite3.wasm path accordingly. If
- sqlite3.js/.wasm are in the same directory as this script then
- that's not needed.
- URL arguments passed as part of the filename via importScripts()
- are simply lost, and such scripts see the globalThis.location of
- _this_ script.
- */
- let sqlite3Js = 'sqlite3.js';
- const urlParams = new URL(globalThis.location.href).searchParams;
- if(urlParams.has('sqlite3.dir')){
- sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
- }
- importScripts(sqlite3Js);
- }
- globalThis.sqlite3InitModule({
- /* We can redirect any stdout/stderr from the module like so, but
- note that doing so makes use of Emscripten-isms, not
- well-defined sqlite APIs. */
- print: log,
- printErr: error
- }).then(function(sqlite3){
- //console.log('sqlite3 =',sqlite3);
- log("Done initializing. Running demo...");
- try {
- demo1(sqlite3);
- }catch(e){
- error("Exception:",e.message);
- }
- });
- })();
|