1/* 2 2022-08-23 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 Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based 14 proxy for for the sqlite3 Worker #1 API. 15*/ 16'use strict'; 17(function(){ 18 const T = self.SqliteTestUtil; 19 const eOutput = document.querySelector('#test-output'); 20 const warn = console.warn.bind(console); 21 const error = console.error.bind(console); 22 const log = console.log.bind(console); 23 const logHtml = async function(cssClass,...args){ 24 log.apply(this, args); 25 const ln = document.createElement('div'); 26 if(cssClass) ln.classList.add(cssClass); 27 ln.append(document.createTextNode(args.join(' '))); 28 eOutput.append(ln); 29 }; 30 31 let startTime; 32 const testCount = async ()=>{ 33 logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); 34 }; 35 36 //why is this triggered even when we catch() a Promise? 37 //window.addEventListener('unhandledrejection', function(event) { 38 // warn('unhandledrejection',event); 39 //}); 40 41 const promiserConfig = { 42 worker: ()=>{ 43 const w = new Worker("jswasm/sqlite3-worker1.js"); 44 w.onerror = (event)=>error("worker.onerror",event); 45 return w; 46 }, 47 debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args), 48 onunhandled: function(ev){ 49 error("Unhandled worker message:",ev.data); 50 }, 51 onready: function(){ 52 self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; 53 runTests(); 54 }, 55 onerror: function(ev){ 56 error("worker1 error:",ev); 57 } 58 }; 59 const workerPromise = self.sqlite3Worker1Promiser(promiserConfig); 60 delete self.sqlite3Worker1Promiser; 61 62 const wtest = async function(msgType, msgArgs, callback){ 63 if(2===arguments.length && 'function'===typeof msgArgs){ 64 callback = msgArgs; 65 msgArgs = undefined; 66 } 67 const p = workerPromise({type: msgType, args:msgArgs}); 68 return callback ? p.then(callback).finally(testCount) : p; 69 }; 70 71 const runTests = async function(){ 72 const dbFilename = '/testing2.sqlite3'; 73 startTime = performance.now(); 74 75 let sqConfig; 76 await wtest('config-get', (ev)=>{ 77 const r = ev.result; 78 log('sqlite3.config subset:', r); 79 T.assert('boolean' === typeof r.bigIntEnabled) 80 .assert('string'===typeof r.wasmfsOpfsDir) 81 .assert('boolean' === typeof r.wasmfsOpfsEnabled); 82 sqConfig = r; 83 }); 84 logHtml('', 85 "Sending 'open' message and waiting for its response before continuing..."); 86 87 await wtest('open', { 88 filename: dbFilename, 89 simulateError: 0 /* if true, fail the 'open' */, 90 }, function(ev){ 91 const r = ev.result; 92 log("then open result",r); 93 T.assert(ev.dbId === r.dbId) 94 .assert(ev.messageId) 95 .assert('string' === typeof r.vfs); 96 promiserConfig.dbId = ev.dbId; 97 }).then(runTests2); 98 }; 99 100 const runTests2 = async function(){ 101 const mustNotReach = ()=>toss("This is not supposed to be reached."); 102 103 await wtest('exec',{ 104 sql: ["create table t(a,b)", 105 "insert into t(a,b) values(1,2),(3,4),(5,6)" 106 ].join(';'), 107 multi: true, 108 resultRows: [], columnNames: [] 109 }, function(ev){ 110 ev = ev.result; 111 T.assert(0===ev.resultRows.length) 112 .assert(0===ev.columnNames.length); 113 }); 114 115 await wtest('exec',{ 116 sql: 'select a a, b b from t order by a', 117 resultRows: [], columnNames: [], 118 }, function(ev){ 119 ev = ev.result; 120 T.assert(3===ev.resultRows.length) 121 .assert(1===ev.resultRows[0][0]) 122 .assert(6===ev.resultRows[2][1]) 123 .assert(2===ev.columnNames.length) 124 .assert('b'===ev.columnNames[1]); 125 }); 126 127 await wtest('exec',{ 128 sql: 'select a a, b b from t order by a', 129 resultRows: [], columnNames: [], 130 rowMode: 'object' 131 }, function(ev){ 132 ev = ev.result; 133 T.assert(3===ev.resultRows.length) 134 .assert(1===ev.resultRows[0].a) 135 .assert(6===ev.resultRows[2].b) 136 }); 137 138 await wtest( 139 'exec', 140 {sql:'intentional_error'}, 141 mustNotReach 142 ).catch((e)=>{ 143 warn("Intentional error:",e); 144 }); 145 146 await wtest('exec',{ 147 sql:'select 1 union all select 3', 148 resultRows: [], 149 }, function(ev){ 150 ev = ev.result; 151 T.assert(2 === ev.resultRows.length) 152 .assert(1 === ev.resultRows[0][0]) 153 .assert(3 === ev.resultRows[1][0]); 154 }); 155 156 const resultRowTest1 = function f(ev){ 157 if(undefined === f.counter) f.counter = 0; 158 if(null === ev.rowNumber){ 159 /* End of result set. */ 160 T.assert(undefined === ev.row) 161 .assert(2===ev.columnNames.length) 162 .assert('a'===ev.columnNames[0]) 163 .assert('B'===ev.columnNames[1]); 164 }else{ 165 T.assert(ev.rowNumber > 0); 166 ++f.counter; 167 } 168 log("exec() result row:",ev); 169 T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B); 170 }; 171 await wtest('exec',{ 172 sql: 'select a a, b B from t order by a limit 3', 173 callback: resultRowTest1, 174 rowMode: 'object' 175 }, function(ev){ 176 T.assert(3===resultRowTest1.counter); 177 resultRowTest1.counter = 0; 178 }); 179 180 const resultRowTest2 = function f(ev){ 181 if(null === ev.rowNumber){ 182 /* End of result set. */ 183 T.assert(undefined === ev.row) 184 .assert(1===ev.columnNames.length) 185 .assert('a'===ev.columnNames[0]) 186 }else{ 187 T.assert(ev.rowNumber > 0); 188 f.counter = ev.rowNumber; 189 } 190 log("exec() result row:",ev); 191 T.assert(null === ev.rowNumber || 'number' === typeof ev.row); 192 }; 193 await wtest('exec',{ 194 sql: 'select a a from t limit 3', 195 callback: resultRowTest2, 196 rowMode: 0 197 }, function(ev){ 198 T.assert(3===resultRowTest2.counter); 199 }); 200 201 const resultRowTest3 = function f(ev){ 202 if(null === ev.rowNumber){ 203 T.assert(3===ev.columnNames.length) 204 .assert('foo'===ev.columnNames[0]) 205 .assert('bar'===ev.columnNames[1]) 206 .assert('baz'===ev.columnNames[2]); 207 }else{ 208 f.counter = ev.rowNumber; 209 T.assert('number' === typeof ev.row); 210 } 211 }; 212 await wtest('exec',{ 213 sql: "select 'foo' foo, a bar, 'baz' baz from t limit 2", 214 callback: resultRowTest3, 215 columnNames: [], 216 rowMode: ':bar' 217 }, function(ev){ 218 log("exec() result row:",ev); 219 T.assert(2===resultRowTest3.counter); 220 }); 221 222 await wtest('exec',{ 223 multi: true, 224 sql:[ 225 'pragma foreign_keys=0;', 226 // ^^^ arbitrary query with no result columns 227 'select a, b from t order by a desc; select a from t;' 228 // multi-exec only honors results from the first 229 // statement with result columns (regardless of whether) 230 // it has any rows). 231 ], 232 rowMode: 1, 233 resultRows: [] 234 },function(ev){ 235 const rows = ev.result.resultRows; 236 T.assert(3===rows.length). 237 assert(6===rows[0]); 238 }); 239 240 await wtest('exec',{sql: 'delete from t where a>3'}); 241 242 await wtest('exec',{ 243 sql: 'select count(a) from t', 244 resultRows: [] 245 },function(ev){ 246 ev = ev.result; 247 T.assert(1===ev.resultRows.length) 248 .assert(2===ev.resultRows[0][0]); 249 }); 250 251 await wtest('export', function(ev){ 252 ev = ev.result; 253 T.assert('string' === typeof ev.filename) 254 .assert(ev.byteArray instanceof Uint8Array) 255 .assert(ev.byteArray.length > 1024) 256 .assert('application/x-sqlite3' === ev.mimetype); 257 }); 258 259 /***** close() tests must come last. *****/ 260 await wtest('close',{},function(ev){ 261 T.assert('string' === typeof ev.result.filename); 262 }); 263 264 await wtest('close', (ev)=>{ 265 T.assert(undefined === ev.result.filename); 266 }).finally(()=>logHtml('',"That's all, folks!")); 267 }/*runTests2()*/; 268 269 log("Init complete, but async init bits may still be running."); 270})(); 271