4 The author disclaims copyright to this source code. In place of a
5 legal notice, here is a blessing:
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.
11 ***********************************************************************
13 A basic demonstration of the SQLite3 "OO#1" API.
18 Set up our output channel differently depending
19 on whether we are running in a worker thread or
23 if(globalThis
.window
=== globalThis
/* 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
);
31 }else{ /* Worker thread */
32 console
.log("Running demo from Worker thread.");
33 logHtml = function(cssClass
,...args
){
36 payload
:{cssClass
, args
}
40 const log
= (...args
)=>logHtml('',...args
);
41 const warn
= (...args
)=>logHtml('warning',...args
);
42 const error
= (...args
)=>logHtml('error',...args
);
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
);
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.
59 log("Create a table...");
60 db
.exec("CREATE TABLE IF NOT EXISTS t(a,b)");
63 sql
:"CREATE TABLE IF NOT EXISTS t(a,b)"
64 // ... numerous other options ...
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
71 log("Insert some data using exec()...");
73 for( i
= 20; i
<= 25; ++i
){
75 sql
: "insert into t(a,b) values (?,?)",
76 // bind by parameter index...
80 sql
: "insert into t(a,b) values ($a,$b)",
81 // bind by parameter name...
82 bind
: {$a
: i
* 10, $b
: i
* 20}
86 log("Insert using a prepared statement...");
88 // SQL may be a string or array of strings
89 // (concatenated w/o separators).
90 "insert into t(a,b) ",
94 for( i
= 100; i
< 103; ++i
){
95 q
.bind( [i
, i
*2] ).step();
99 for( i
= 103; i
<= 105; ++i
){
100 q
.bind(1, i
).bind(2, i
*2).stepReset();
106 log("Query data with exec() using rowMode 'array'...");
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
);
115 log("Query data with exec() using rowMode 'object'...");
117 sql
: "select a as aa, b as bb from t order by aa limit 3",
119 callback: function(row
){
120 log("row ",++this.counter
,"=",JSON
.stringify(row
));
124 log("Query data with exec() using rowMode 'stmt'...");
126 sql
: "select a from t order by a limit 3",
128 callback: function(row
){
129 log("row ",++this.counter
,"get(0) =",row
.get(0));
133 log("Query data with exec() using rowMode INTEGER (result column index)...");
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
);
142 log("Query data with exec() using rowMode $COLNAME (result column name)...");
144 sql
: "select a a, b from t order by a limit 3",
146 callback: function(value
){
147 log("row ",++this.counter
,"a =",value
);
151 log("Query data with exec() without a callback...");
154 sql
: "select a, b from t order by a limit 3",
156 resultRows
: resultRows
158 log("Result rows:",JSON
.stringify(resultRows
,undefined,2));
160 log("Create a scalar UDF...");
163 xFunc: function(pCx
, arg
){ // note the call arg count
167 log("Run scalar UDF and collect result column names...");
168 let columnNames
= [];
170 sql
: "select a, twice(a), twice(''||a) from t order by a desc limit 3",
171 columnNames
: columnNames
,
173 callback: function(row
){
174 log("a =",row
.get(0), "twice(a) =", row
.get(1),
175 "twice(''||a) =",row
.get(2));
178 log("Result column names:",columnNames
);
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)");
185 warn("Got expected exception:",e
.message
);
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");
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"));
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
){
210 sql
: ["insert into t(a,b) values(99,100);",
211 "select count(*) from t"],
215 log("In nested savepoint. Row count =",rows
[0]);
216 throw new sqlite3
.SQLite3Error("Demonstrating nested savepoint() rollback");
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"));
231 log("That's all, folks!");
234 Some of the features of the OO API not demonstrated above...
236 - get change count (total or statement-local, 32- or 64-bit)
237 - get a DB's file name
241 - Various forms of bind()
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)
252 log("Loading and initializing sqlite3 module...");
253 if(globalThis
.window
!==globalThis
) /*worker thread*/{
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
264 URL arguments passed as part of the filename via importScripts()
265 are simply lost, and such scripts see the globalThis.location of
268 let sqlite3Js
= 'sqlite3.js';
269 const urlParams
= new URL(globalThis
.location
.href
).searchParams
;
270 if(urlParams
.has('sqlite3.dir')){
271 sqlite3Js
= urlParams
.get('sqlite3.dir') + '/' + sqlite3Js
;
273 importScripts(sqlite3Js
);
275 globalThis
.sqlite3InitModule({
276 /* We can redirect any stdout/stderr from the module like so, but
277 note that doing so makes use of Emscripten-isms, not
278 well-defined sqlite APIs. */
281 }).then(function(sqlite3
){
282 //console.log('sqlite3 =',sqlite3);
283 log("Done initializing. Running demo...");
287 error("Exception:",e
.message
);