1/* 2 2022-07-22 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 This file contains the so-called OO #1 API wrapper for the sqlite3 14 WASM build. It requires that sqlite3-api-glue.js has already run 15 and it installs its deliverable as self.sqlite3.oo1. 16*/ 17self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 18 const toss = (...args)=>{throw new Error(args.join(' '))}; 19 const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)}; 20 21 const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; 22 /* What follows is colloquially known as "OO API #1". It is a 23 binding of the sqlite3 API which is designed to be run within 24 the same thread (main or worker) as the one in which the 25 sqlite3 WASM binding was initialized. This wrapper cannot use 26 the sqlite3 binding if, e.g., the wrapper is in the main thread 27 and the sqlite3 API is in a worker. */ 28 29 /** 30 In order to keep clients from manipulating, perhaps 31 inadvertently, the underlying pointer values of DB and Stmt 32 instances, we'll gate access to them via the `pointer` property 33 accessor and store their real values in this map. Keys = DB/Stmt 34 objects, values = pointer values. This also unifies how those are 35 accessed, for potential use downstream via custom 36 wasm.xWrap() function signatures which know how to extract 37 it. 38 */ 39 const __ptrMap = new WeakMap(); 40 /** 41 Map of DB instances to objects, each object being a map of Stmt 42 wasm pointers to Stmt objects. 43 */ 44 const __stmtMap = new WeakMap(); 45 46 /** If object opts has _its own_ property named p then that 47 property's value is returned, else dflt is returned. */ 48 const getOwnOption = (opts, p, dflt)=>{ 49 const d = Object.getOwnPropertyDescriptor(opts,p); 50 return d ? d.value : dflt; 51 }; 52 53 // Documented in DB.checkRc() 54 const checkSqlite3Rc = function(dbPtr, sqliteResultCode){ 55 if(sqliteResultCode){ 56 if(dbPtr instanceof DB) dbPtr = dbPtr.pointer; 57 toss3( 58 "sqlite result code",sqliteResultCode+":", 59 (dbPtr 60 ? capi.sqlite3_errmsg(dbPtr) 61 : capi.sqlite3_errstr(sqliteResultCode)) 62 ); 63 } 64 }; 65 66 /** 67 sqlite3_trace_v2() callback which gets installed by the DB ctor 68 if its open-flags contain "t". 69 */ 70 const __dbTraceToConsole = 71 wasm.installFunction('i(ippp)', function(t,c,p,x){ 72 if(capi.SQLITE_TRACE_STMT===t){ 73 // x == SQL, p == sqlite3_stmt* 74 console.log("SQL TRACE #"+(++this.counter), 75 wasm.cstringToJs(x)); 76 } 77 }.bind({counter: 0})); 78 79 /** 80 A map of sqlite3_vfs pointers to SQL code to run when the DB 81 constructor opens a database with the given VFS. 82 */ 83 const __vfsPostOpenSql = Object.create(null); 84 85 /** 86 A proxy for DB class constructors. It must be called with the 87 being-construct DB object as its "this". See the DB constructor 88 for the argument docs. This is split into a separate function 89 in order to enable simple creation of special-case DB constructors, 90 e.g. JsStorageDb and OpfsDb. 91 92 Expects to be passed a configuration object with the following 93 properties: 94 95 - `.filename`: the db filename. It may be a special name like ":memory:" 96 or "". 97 98 - `.flags`: as documented in the DB constructor. 99 100 - `.vfs`: as documented in the DB constructor. 101 102 It also accepts those as the first 3 arguments. 103 */ 104 const dbCtorHelper = function ctor(...args){ 105 if(!ctor._name2vfs){ 106 /** 107 Map special filenames which we handle here (instead of in C) 108 to some helpful metadata... 109 110 As of 2022-09-20, the C API supports the names :localStorage: 111 and :sessionStorage: for kvvfs. However, C code cannot 112 determine (without embedded JS code, e.g. via Emscripten's 113 EM_JS()) whether the kvvfs is legal in the current browser 114 context (namely the main UI thread). In order to help client 115 code fail early on, instead of it being delayed until they 116 try to read or write a kvvfs-backed db, we'll check for those 117 names here and throw if they're not legal in the current 118 context. 119 */ 120 ctor._name2vfs = Object.create(null); 121 const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/) 122 ? (n)=>toss3("The VFS for",n,"is only available in the main window thread.") 123 : false; 124 ctor._name2vfs[':localStorage:'] = { 125 vfs: 'kvvfs', filename: isWorkerThread || (()=>'local') 126 }; 127 ctor._name2vfs[':sessionStorage:'] = { 128 vfs: 'kvvfs', filename: isWorkerThread || (()=>'session') 129 }; 130 } 131 const opt = ctor.normalizeArgs(...args); 132 let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; 133 if(('string'!==typeof fn && 'number'!==typeof fn) 134 || 'string'!==typeof flagsStr 135 || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){ 136 console.error("Invalid DB ctor args",opt,arguments); 137 toss3("Invalid arguments for DB constructor."); 138 } 139 let fnJs = ('number'===typeof fn) ? wasm.cstringToJs(fn) : fn; 140 const vfsCheck = ctor._name2vfs[fnJs]; 141 if(vfsCheck){ 142 vfsName = vfsCheck.vfs; 143 fn = fnJs = vfsCheck.filename(fnJs); 144 } 145 let pDb, oflags = 0; 146 if( flagsStr.indexOf('c')>=0 ){ 147 oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; 148 } 149 if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; 150 if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; 151 oflags |= capi.SQLITE_OPEN_EXRESCODE; 152 const stack = wasm.pstack.pointer; 153 try { 154 const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */; 155 let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0); 156 pDb = wasm.getPtrValue(pPtr); 157 checkSqlite3Rc(pDb, rc); 158 if(flagsStr.indexOf('t')>=0){ 159 capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, 160 __dbTraceToConsole, 0); 161 } 162 // Check for per-VFS post-open SQL... 163 const pVfs = capi.sqlite3_js_db_vfs(pDb); 164 //console.warn("Opened db",fn,"with vfs",vfsName,pVfs); 165 if(!pVfs) toss3("Internal error: cannot get VFS for new db handle."); 166 const postInitSql = __vfsPostOpenSql[pVfs]; 167 if(postInitSql){ 168 rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0); 169 checkSqlite3Rc(pDb, rc); 170 } 171 }catch( e ){ 172 if( pDb ) capi.sqlite3_close_v2(pDb); 173 throw e; 174 }finally{ 175 wasm.pstack.restore(stack); 176 } 177 this.filename = fnJs; 178 __ptrMap.set(this, pDb); 179 __stmtMap.set(this, Object.create(null)); 180 }; 181 182 /** 183 Sets SQL which should be exec()'d on a DB instance after it is 184 opened with the given VFS pointer. This is intended only for use 185 by DB subclasses or sqlite3_vfs implementations. 186 */ 187 dbCtorHelper.setVfsPostOpenSql = function(pVfs, sql){ 188 __vfsPostOpenSql[pVfs] = sql; 189 }; 190 191 /** 192 A helper for DB constructors. It accepts either a single 193 config-style object or up to 3 arguments (filename, dbOpenFlags, 194 dbVfsName). It returns a new object containing: 195 196 { filename: ..., flags: ..., vfs: ... } 197 198 If passed an object, any additional properties it has are copied 199 as-is into the new object. 200 */ 201 dbCtorHelper.normalizeArgs = function(filename=':memory:',flags = 'c',vfs = null){ 202 const arg = {}; 203 if(1===arguments.length && 'object'===typeof arguments[0]){ 204 const x = arguments[0]; 205 Object.keys(x).forEach((k)=>arg[k] = x[k]); 206 if(undefined===arg.flags) arg.flags = 'c'; 207 if(undefined===arg.vfs) arg.vfs = null; 208 if(undefined===arg.filename) arg.filename = ':memory:'; 209 }else{ 210 arg.filename = filename; 211 arg.flags = flags; 212 arg.vfs = vfs; 213 } 214 return arg; 215 }; 216 /** 217 The DB class provides a high-level OO wrapper around an sqlite3 218 db handle. 219 220 The given db filename must be resolvable using whatever 221 filesystem layer (virtual or otherwise) is set up for the default 222 sqlite3 VFS. 223 224 Note that the special sqlite3 db names ":memory:" and "" 225 (temporary db) have their normal special meanings here and need 226 not resolve to real filenames, but "" uses an on-storage 227 temporary database and requires that the VFS support that. 228 229 The second argument specifies the open/create mode for the 230 database. It must be string containing a sequence of letters (in 231 any order, but case sensitive) specifying the mode: 232 233 - "c": create if it does not exist, else fail if it does not 234 exist. Implies the "w" flag. 235 236 - "w": write. Implies "r": a db cannot be write-only. 237 238 - "r": read-only if neither "w" nor "c" are provided, else it 239 is ignored. 240 241 - "t": enable tracing of SQL executed on this database handle, 242 sending it to `console.log()`. To disable it later, call 243 `sqlite3.capi.sqlite3_trace_v2(thisDb.pointer, 0, 0, 0)`. 244 245 If "w" is not provided, the db is implicitly read-only, noting 246 that "rc" is meaningless 247 248 Any other letters are currently ignored. The default is 249 "c". These modes are ignored for the special ":memory:" and "" 250 names and _may_ be ignored altogether for certain VFSes. 251 252 The final argument is analogous to the final argument of 253 sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, 254 or none at all, to use the default. If passed a value, it must 255 be the string name of a VFS. 256 257 The constructor optionally (and preferably) takes its arguments 258 in the form of a single configuration object with the following 259 properties: 260 261 - `.filename`: database file name 262 - `.flags`: open-mode flags 263 - `.vfs`: the VFS fname 264 265 The `filename` and `vfs` arguments may be either JS strings or 266 C-strings allocated via WASM. `flags` is required to be a JS 267 string (because it's specific to this API, which is specific 268 to JS). 269 270 For purposes of passing a DB instance to C-style sqlite3 271 functions, the DB object's read-only `pointer` property holds its 272 `sqlite3*` pointer value. That property can also be used to check 273 whether this DB instance is still open. 274 275 In the main window thread, the filenames `":localStorage:"` and 276 `":sessionStorage:"` are special: they cause the db to use either 277 localStorage or sessionStorage for storing the database using 278 the kvvfs. If one of these names are used, they trump 279 any vfs name set in the arguments. 280 */ 281 const DB = function(...args){ 282 dbCtorHelper.apply(this, args); 283 }; 284 DB.dbCtorHelper = dbCtorHelper; 285 286 /** 287 Internal-use enum for mapping JS types to DB-bindable types. 288 These do not (and need not) line up with the SQLITE_type 289 values. All values in this enum must be truthy and distinct 290 but they need not be numbers. 291 */ 292 const BindTypes = { 293 null: 1, 294 number: 2, 295 string: 3, 296 boolean: 4, 297 blob: 5 298 }; 299 BindTypes['undefined'] == BindTypes.null; 300 if(wasm.bigIntEnabled){ 301 BindTypes.bigint = BindTypes.number; 302 } 303 304 /** 305 This class wraps sqlite3_stmt. Calling this constructor 306 directly will trigger an exception. Use DB.prepare() to create 307 new instances. 308 309 For purposes of passing a Stmt instance to C-style sqlite3 310 functions, its read-only `pointer` property holds its `sqlite3_stmt*` 311 pointer value. 312 313 Other non-function properties include: 314 315 - `db`: the DB object which created the statement. 316 317 - `columnCount`: the number of result columns in the query, or 0 for 318 queries which cannot return results. 319 320 - `parameterCount`: the number of bindable paramters in the query. 321 */ 322 const Stmt = function(){ 323 if(BindTypes!==arguments[2]){ 324 toss3("Do not call the Stmt constructor directly. Use DB.prepare()."); 325 } 326 this.db = arguments[0]; 327 __ptrMap.set(this, arguments[1]); 328 this.columnCount = capi.sqlite3_column_count(this.pointer); 329 this.parameterCount = capi.sqlite3_bind_parameter_count(this.pointer); 330 }; 331 332 /** Throws if the given DB has been closed, else it is returned. */ 333 const affirmDbOpen = function(db){ 334 if(!db.pointer) toss3("DB has been closed."); 335 return db; 336 }; 337 338 /** Throws if ndx is not an integer or if it is out of range 339 for stmt.columnCount, else returns stmt. 340 341 Reminder: this will also fail after the statement is finalized 342 but the resulting error will be about an out-of-bounds column 343 index rather than a statement-is-finalized error. 344 */ 345 const affirmColIndex = function(stmt,ndx){ 346 if((ndx !== (ndx|0)) || ndx<0 || ndx>=stmt.columnCount){ 347 toss3("Column index",ndx,"is out of range."); 348 } 349 return stmt; 350 }; 351 352 /** 353 Expects to be passed the `arguments` object from DB.exec(). Does 354 the argument processing/validation, throws on error, and returns 355 a new object on success: 356 357 { sql: the SQL, opt: optionsObj, cbArg: function} 358 359 The opt object is a normalized copy of any passed to this 360 function. The sql will be converted to a string if it is provided 361 in one of the supported non-string formats. 362 363 cbArg is only set if the opt.callback or opt.resultRows are set, 364 in which case it's a function which expects to be passed the 365 current Stmt and returns the callback argument of the type 366 indicated by the input arguments. 367 */ 368 const parseExecArgs = function(db, args){ 369 const out = Object.create(null); 370 out.opt = Object.create(null); 371 switch(args.length){ 372 case 1: 373 if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){ 374 out.sql = args[0]; 375 }else if(Array.isArray(args[0])){ 376 out.sql = args[0]; 377 }else if(args[0] && 'object'===typeof args[0]){ 378 out.opt = args[0]; 379 out.sql = out.opt.sql; 380 } 381 break; 382 case 2: 383 out.sql = args[0]; 384 out.opt = args[1]; 385 break; 386 default: toss3("Invalid argument count for exec()."); 387 }; 388 out.sql = util.flexibleString(out.sql); 389 if('string'!==typeof out.sql){ 390 toss3("Missing SQL argument or unsupported SQL value type."); 391 } 392 const opt = out.opt; 393 switch(opt.returnValue){ 394 case 'resultRows': 395 if(!opt.resultRows) opt.resultRows = []; 396 out.returnVal = ()=>opt.resultRows; 397 break; 398 case 'saveSql': 399 if(!opt.saveSql) opt.saveSql = []; 400 out.returnVal = ()=>opt.saveSql; 401 break; 402 case undefined: 403 case 'this': 404 break; 405 default: 406 toss3("Invalid returnValue value:",opt.returnValue); 407 } 408 if(!out.returnVal) out.returnVal = ()=>db; 409 if(opt.callback || opt.resultRows){ 410 switch((undefined===opt.rowMode) 411 ? 'array' : opt.rowMode) { 412 case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break; 413 case 'array': out.cbArg = (stmt)=>stmt.get([]); break; 414 case 'stmt': 415 if(Array.isArray(opt.resultRows)){ 416 toss3("exec(): invalid rowMode for a resultRows array: must", 417 "be one of 'array', 'object',", 418 "a result column number, or column name reference."); 419 } 420 out.cbArg = (stmt)=>stmt; 421 break; 422 default: 423 if(util.isInt32(opt.rowMode)){ 424 out.cbArg = (stmt)=>stmt.get(opt.rowMode); 425 break; 426 }else if('string'===typeof opt.rowMode && opt.rowMode.length>1){ 427 /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */ 428 const prefix = opt.rowMode[0]; 429 if(':'===prefix || '@'===prefix || '$'===prefix){ 430 out.cbArg = function(stmt){ 431 const rc = stmt.get(this.obj)[this.colName]; 432 return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc; 433 }.bind({ 434 obj:Object.create(null), 435 colName: opt.rowMode.substr(1) 436 }); 437 break; 438 } 439 } 440 toss3("Invalid rowMode:",opt.rowMode); 441 } 442 } 443 return out; 444 }; 445 446 /** 447 Internal impl of the DB.selectArray() and 448 selectObject() methods. 449 */ 450 const __selectFirstRow = (db, sql, bind, getArg)=>{ 451 let stmt, rc; 452 try { 453 stmt = db.prepare(sql).bind(bind); 454 if(stmt.step()) rc = stmt.get(getArg); 455 }finally{ 456 if(stmt) stmt.finalize(); 457 } 458 return rc; 459 }; 460 461 /** 462 Expects to be given a DB instance or an `sqlite3*` pointer (may 463 be null) and an sqlite3 API result code. If the result code is 464 not falsy, this function throws an SQLite3Error with an error 465 message from sqlite3_errmsg(), using dbPtr as the db handle, or 466 sqlite3_errstr() if dbPtr is falsy. Note that if it's passed a 467 non-error code like SQLITE_ROW or SQLITE_DONE, it will still 468 throw but the error string might be "Not an error." The various 469 non-0 non-error codes need to be checked for in 470 client code where they are expected. 471 */ 472 DB.checkRc = checkSqlite3Rc; 473 474 DB.prototype = { 475 /** Returns true if this db handle is open, else false. */ 476 isOpen: function(){ 477 return !!this.pointer; 478 }, 479 /** Throws if this given DB has been closed, else returns `this`. */ 480 affirmOpen: function(){ 481 return affirmDbOpen(this); 482 }, 483 /** 484 Finalizes all open statements and closes this database 485 connection. This is a no-op if the db has already been 486 closed. After calling close(), `this.pointer` will resolve to 487 `undefined`, so that can be used to check whether the db 488 instance is still opened. 489 490 If this.onclose.before is a function then it is called before 491 any close-related cleanup. 492 493 If this.onclose.after is a function then it is called after the 494 db is closed but before auxiliary state like this.filename is 495 cleared. 496 497 Both onclose handlers are passed this object. If this db is not 498 opened, neither of the handlers are called. Any exceptions the 499 handlers throw are ignored because "destructors must not 500 throw." 501 502 Note that garbage collection of a db handle, if it happens at 503 all, will never trigger close(), so onclose handlers are not a 504 reliable way to implement close-time cleanup or maintenance of 505 a db. 506 */ 507 close: function(){ 508 if(this.pointer){ 509 if(this.onclose && (this.onclose.before instanceof Function)){ 510 try{this.onclose.before(this)} 511 catch(e){/*ignore*/} 512 } 513 const pDb = this.pointer; 514 Object.keys(__stmtMap.get(this)).forEach((k,s)=>{ 515 if(s && s.pointer) s.finalize(); 516 }); 517 __ptrMap.delete(this); 518 __stmtMap.delete(this); 519 capi.sqlite3_close_v2(pDb); 520 if(this.onclose && (this.onclose.after instanceof Function)){ 521 try{this.onclose.after(this)} 522 catch(e){/*ignore*/} 523 } 524 delete this.filename; 525 } 526 }, 527 /** 528 Returns the number of changes, as per sqlite3_changes() 529 (if the first argument is false) or sqlite3_total_changes() 530 (if it's true). If the 2nd argument is true, it uses 531 sqlite3_changes64() or sqlite3_total_changes64(), which 532 will trigger an exception if this build does not have 533 BigInt support enabled. 534 */ 535 changes: function(total=false,sixtyFour=false){ 536 const p = affirmDbOpen(this).pointer; 537 if(total){ 538 return sixtyFour 539 ? capi.sqlite3_total_changes64(p) 540 : capi.sqlite3_total_changes(p); 541 }else{ 542 return sixtyFour 543 ? capi.sqlite3_changes64(p) 544 : capi.sqlite3_changes(p); 545 } 546 }, 547 /** 548 Similar to the this.filename but returns the 549 sqlite3_db_filename() value for the given database name, 550 defaulting to "main". The argument may be either a JS string 551 or a pointer to a WASM-allocated C-string. 552 */ 553 dbFilename: function(dbName='main'){ 554 return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); 555 }, 556 /** 557 Returns the name of the given 0-based db number, as documented 558 for sqlite3_db_name(). 559 */ 560 dbName: function(dbNumber=0){ 561 return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); 562 }, 563 /** 564 Compiles the given SQL and returns a prepared Stmt. This is 565 the only way to create new Stmt objects. Throws on error. 566 567 The given SQL must be a string, a Uint8Array holding SQL, a 568 WASM pointer to memory holding the NUL-terminated SQL string, 569 or an array of strings. In the latter case, the array is 570 concatenated together, with no separators, to form the SQL 571 string (arrays are often a convenient way to formulate long 572 statements). If the SQL contains no statements, an 573 SQLite3Error is thrown. 574 575 Design note: the C API permits empty SQL, reporting it as a 0 576 result code and a NULL stmt pointer. Supporting that case here 577 would cause extra work for all clients: any use of the Stmt API 578 on such a statement will necessarily throw, so clients would be 579 required to check `stmt.pointer` after calling `prepare()` in 580 order to determine whether the Stmt instance is empty or not. 581 Long-time practice (with other sqlite3 script bindings) 582 suggests that the empty-prepare case is sufficiently rare that 583 supporting it here would simply hurt overall usability. 584 */ 585 prepare: function(sql){ 586 affirmDbOpen(this); 587 const stack = wasm.pstack.pointer; 588 let ppStmt, pStmt; 589 try{ 590 ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */; 591 DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); 592 pStmt = wasm.getPtrValue(ppStmt); 593 } 594 finally { 595 wasm.pstack.restore(stack); 596 } 597 if(!pStmt) toss3("Cannot prepare empty SQL."); 598 const stmt = new Stmt(this, pStmt, BindTypes); 599 __stmtMap.get(this)[pStmt] = stmt; 600 return stmt; 601 }, 602 /** 603 Executes one or more SQL statements in the form of a single 604 string. Its arguments must be either (sql,optionsObject) or 605 (optionsObject). In the latter case, optionsObject.sql must 606 contain the SQL to execute. By default it returns this object 607 but that can be changed via the `returnValue` option as 608 described below. Throws on error. 609 610 If no SQL is provided, or a non-string is provided, an 611 exception is triggered. Empty SQL, on the other hand, is 612 simply a no-op. 613 614 The optional options object may contain any of the following 615 properties: 616 617 - `sql` = the SQL to run (unless it's provided as the first 618 argument). This must be of type string, Uint8Array, or an array 619 of strings. In the latter case they're concatenated together 620 as-is, _with no separator_ between elements, before evaluation. 621 The array form is often simpler for long hand-written queries. 622 623 - `bind` = a single value valid as an argument for 624 Stmt.bind(). This is _only_ applied to the _first_ non-empty 625 statement in the SQL which has any bindable parameters. (Empty 626 statements are skipped entirely.) 627 628 - `saveSql` = an optional array. If set, the SQL of each 629 executed statement is appended to this array before the 630 statement is executed (but after it is prepared - we don't have 631 the string until after that). Empty SQL statements are elided 632 but can have odd effects in the output. e.g. SQL of: `"select 633 1; -- empty\n; select 2"` will result in an array containing 634 `["select 1;", "--empty \n; select 2"]`. That's simply how 635 sqlite3 records the SQL for the 2nd statement. 636 637 ================================================================== 638 The following options apply _only_ to the _first_ statement 639 which has a non-zero result column count, regardless of whether 640 the statement actually produces any result rows. 641 ================================================================== 642 643 - `columnNames`: if this is an array, the column names of the 644 result set are stored in this array before the callback (if 645 any) is triggered (regardless of whether the query produces any 646 result rows). If no statement has result columns, this value is 647 unchanged. Achtung: an SQL result may have multiple columns 648 with identical names. 649 650 - `callback` = a function which gets called for each row of 651 the result set, but only if that statement has any result 652 _rows_. The callback's "this" is the options object, noting 653 that this function synthesizes one if the caller does not pass 654 one to exec(). The second argument passed to the callback is 655 always the current Stmt object, as it's needed if the caller 656 wants to fetch the column names or some such (noting that they 657 could also be fetched via `this.columnNames`, if the client 658 provides the `columnNames` option). 659 660 ACHTUNG: The callback MUST NOT modify the Stmt object. Calling 661 any of the Stmt.get() variants, Stmt.getColumnName(), or 662 similar, is legal, but calling step() or finalize() is 663 not. Member methods which are illegal in this context will 664 trigger an exception. 665 666 The first argument passed to the callback defaults to an array of 667 values from the current result row but may be changed with ... 668 669 - `rowMode` = specifies the type of he callback's first argument. 670 It may be any of... 671 672 A) A string describing what type of argument should be passed 673 as the first argument to the callback: 674 675 A.1) `'array'` (the default) causes the results of 676 `stmt.get([])` to be passed to the `callback` and/or appended 677 to `resultRows` 678 679 A.2) `'object'` causes the results of 680 `stmt.get(Object.create(null))` to be passed to the 681 `callback` and/or appended to `resultRows`. Achtung: an SQL 682 result may have multiple columns with identical names. In 683 that case, the right-most column will be the one set in this 684 object! 685 686 A.3) `'stmt'` causes the current Stmt to be passed to the 687 callback, but this mode will trigger an exception if 688 `resultRows` is an array because appending the statement to 689 the array would be downright unhelpful. 690 691 B) An integer, indicating a zero-based column in the result 692 row. Only that one single value will be passed on. 693 694 C) A string with a minimum length of 2 and leading character of 695 ':', '$', or '@' will fetch the row as an object, extract that 696 one field, and pass that field's value to the callback. Note 697 that these keys are case-sensitive so must match the case used 698 in the SQL. e.g. `"select a A from t"` with a `rowMode` of 699 `'$A'` would work but `'$a'` would not. A reference to a column 700 not in the result set will trigger an exception on the first 701 row (as the check is not performed until rows are fetched). 702 Note also that `$` is a legal identifier character in JS so 703 need not be quoted. (Design note: those 3 characters were 704 chosen because they are the characters support for naming bound 705 parameters.) 706 707 Any other `rowMode` value triggers an exception. 708 709 - `resultRows`: if this is an array, it functions similarly to 710 the `callback` option: each row of the result set (if any), 711 with the exception that the `rowMode` 'stmt' is not legal. It 712 is legal to use both `resultRows` and `callback`, but 713 `resultRows` is likely much simpler to use for small data sets 714 and can be used over a WebWorker-style message interface. 715 exec() throws if `resultRows` is set and `rowMode` is 'stmt'. 716 717 - `returnValue`: is a string specifying what this function 718 should return: 719 720 A) The default value is `"this"`, meaning that the 721 DB object itself should be returned. 722 723 B) `"resultRows"` means to return the value of the 724 `resultRows` option. If `resultRows` is not set, this 725 function behaves as if it were set to an empty array. 726 727 C) `"saveSql"` means to return the value of the 728 `saveSql` option. If `saveSql` is not set, this 729 function behaves as if it were set to an empty array. 730 731 Potential TODOs: 732 733 - `bind`: permit an array of arrays/objects to bind. The first 734 sub-array would act on the first statement which has bindable 735 parameters (as it does now). The 2nd would act on the next such 736 statement, etc. 737 738 - `callback` and `resultRows`: permit an array entries with 739 semantics similar to those described for `bind` above. 740 741 */ 742 exec: function(/*(sql [,obj]) || (obj)*/){ 743 affirmDbOpen(this); 744 const arg = parseExecArgs(this, arguments); 745 if(!arg.sql){ 746 return (''===arg.sql) ? this : toss3("exec() requires an SQL string."); 747 } 748 const opt = arg.opt; 749 const callback = opt.callback; 750 const returnValue = opt.returnValue || 'this'; 751 const resultRows = (Array.isArray(opt.resultRows) 752 ? opt.resultRows : ( 753 'resultRows'===returnValue ? [] : undefined 754 )); 755 let stmt; 756 let bind = opt.bind; 757 let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; 758 const stack = wasm.scopedAllocPush(); 759 try{ 760 const isTA = util.isSQLableTypedArray(arg.sql) 761 /* Optimization: if the SQL is a TypedArray we can save some string 762 conversion costs. */; 763 /* Allocate the two output pointers (ppStmt, pzTail) and heap 764 space for the SQL (pSql). When prepare_v2() returns, pzTail 765 will point to somewhere in pSql. */ 766 let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); 767 const ppStmt = wasm.scopedAlloc(/* output (sqlite3_stmt**) arg and pzTail */ 768 (2 * wasm.ptrSizeof) 769 + (sqlByteLen + 1/* SQL + NUL */)); 770 const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */; 771 let pSql = pzTail + wasm.ptrSizeof; 772 const pSqlEnd = pSql + sqlByteLen; 773 if(isTA) wasm.heap8().set(arg.sql, pSql); 774 else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); 775 wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/); 776 while(pSql && wasm.getMemValue(pSql, 'i8') 777 /* Maintenance reminder:^^^ _must_ be 'i8' or else we 778 will very likely cause an endless loop. What that's 779 doing is checking for a terminating NUL byte. If we 780 use i32 or similar then we read 4 bytes, read stuff 781 around the NUL terminator, and get stuck in and 782 endless loop at the end of the SQL, endlessly 783 re-preparing an empty statement. */ ){ 784 wasm.setPtrValue(ppStmt, 0); 785 wasm.setPtrValue(pzTail, 0); 786 DB.checkRc(this, capi.sqlite3_prepare_v3( 787 this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail 788 )); 789 const pStmt = wasm.getPtrValue(ppStmt); 790 pSql = wasm.getPtrValue(pzTail); 791 sqlByteLen = pSqlEnd - pSql; 792 if(!pStmt) continue; 793 if(Array.isArray(opt.saveSql)){ 794 opt.saveSql.push(capi.sqlite3_sql(pStmt).trim()); 795 } 796 stmt = new Stmt(this, pStmt, BindTypes); 797 if(bind && stmt.parameterCount){ 798 stmt.bind(bind); 799 bind = null; 800 } 801 if(evalFirstResult && stmt.columnCount){ 802 /* Only forward SELECT results for the FIRST query 803 in the SQL which potentially has them. */ 804 evalFirstResult = false; 805 if(Array.isArray(opt.columnNames)){ 806 stmt.getColumnNames(opt.columnNames); 807 } 808 while(!!arg.cbArg && stmt.step()){ 809 stmt._isLocked = true; 810 const row = arg.cbArg(stmt); 811 if(resultRows) resultRows.push(row); 812 if(callback) callback.call(opt, row, stmt); 813 stmt._isLocked = false; 814 } 815 }else{ 816 stmt.step(); 817 } 818 stmt.finalize(); 819 stmt = null; 820 } 821 }/*catch(e){ 822 console.warn("DB.exec() is propagating exception",opt,e); 823 throw e; 824 }*/finally{ 825 if(stmt){ 826 delete stmt._isLocked; 827 stmt.finalize(); 828 } 829 wasm.scopedAllocPop(stack); 830 } 831 return arg.returnVal(); 832 }/*exec()*/, 833 /** 834 Creates a new scalar UDF (User-Defined Function) which is 835 accessible via SQL code. This function may be called in any 836 of the following forms: 837 838 - (name, function) 839 - (name, function, optionsObject) 840 - (name, optionsObject) 841 - (optionsObject) 842 843 In the final two cases, the function must be defined as the 844 `callback` property of the options object (optionally called 845 `xFunc` to align with the C API documentation). In the final 846 case, the function's name must be the 'name' property. 847 848 The first two call forms can only be used for creating scalar 849 functions. Creating an aggregate or window function requires 850 the options-object form (see below for details). 851 852 UDFs cannot currently be removed from a DB handle after they're 853 added. More correctly, they can be removed as documented for 854 sqlite3_create_function_v2(), but doing so will "leak" the 855 JS-created WASM binding of those functions. 856 857 On success, returns this object. Throws on error. 858 859 When called from SQL arguments to the UDF, and its result, 860 will be converted between JS and SQL with as much fidelity as 861 is feasible, triggering an exception if a type conversion 862 cannot be determined. The docs for sqlite3_create_function_v2() 863 describe the conversions in more detail. 864 865 The values set in the options object differ for scalar and 866 aggregate functions: 867 868 - Scalar: set the `xFunc` function-type property to the UDF 869 function. 870 871 - Aggregate: set the `xStep` and `xFinal` function-type 872 properties to the "step" and "final" callbacks for the 873 aggregate. Do not set the `xFunc` property. 874 875 - Window: set the `xStep`, `xFinal`, `xValue`, and `xInverse` 876 function-type properties. Do not set the `xFunc` property. 877 878 The options object may optionally have an `xDestroy` 879 function-type property, as per sqlite3_create_function_v2(). 880 Its argument will be the WASM-pointer-type value of the `pApp` 881 property, and this function will throw if `pApp` is defined but 882 is not null, undefined, or a numeric (WASM pointer) 883 value. i.e. `pApp`, if set, must be value suitable for use as a 884 WASM pointer argument, noting that `null` or `undefined` will 885 translate to 0 for that purpose. 886 887 The options object may contain flags to modify how 888 the function is defined: 889 890 - `arity`: the number of arguments which SQL calls to this 891 function expect or require. The default value is `xFunc.length` 892 or `xStep.length` (i.e. the number of declared parameters it 893 has) **MINUS 1** (see below for why). As a special case, if the 894 `length` is 0, its arity is also 0 instead of -1. A negative 895 arity value means that the function is variadic and may accept 896 any number of arguments, up to sqlite3's compile-time 897 limits. sqlite3 will enforce the argument count if is zero or 898 greater. The callback always receives a pointer to an 899 `sqlite3_context` object as its first argument. Any arguments 900 after that are from SQL code. The leading context argument does 901 _not_ count towards the function's arity. See the docs for 902 sqlite3.capi.sqlite3_create_function_v2() for why that argument 903 is needed in the interface. 904 905 The following options-object properties correspond to flags 906 documented at: 907 908 https://sqlite.org/c3ref/create_function.html 909 910 - `deterministic` = sqlite3.capi.SQLITE_DETERMINISTIC 911 - `directOnly` = sqlite3.capi.SQLITE_DIRECTONLY 912 - `innocuous` = sqlite3.capi.SQLITE_INNOCUOUS 913 914 Sidebar: the ability to add new WASM-accessible functions to 915 the runtime requires that the WASM build is compiled with the 916 equivalent functionality as that provided by Emscripten's 917 `-sALLOW_TABLE_GROWTH` flag. 918 */ 919 createFunction: function f(name, xFunc, opt){ 920 const isFunc = (f)=>(f instanceof Function); 921 switch(arguments.length){ 922 case 1: /* (optionsObject) */ 923 opt = name; 924 name = opt.name; 925 xFunc = opt.xFunc || 0; 926 break; 927 case 2: /* (name, callback|optionsObject) */ 928 if(!isFunc(xFunc)){ 929 opt = xFunc; 930 xFunc = opt.xFunc || 0; 931 } 932 break; 933 case 3: /* name, xFunc, opt */ 934 break; 935 default: break; 936 } 937 if(!opt) opt = {}; 938 if('string' !== typeof name){ 939 toss3("Invalid arguments: missing function name."); 940 } 941 let xStep = opt.xStep || 0; 942 let xFinal = opt.xFinal || 0; 943 const xValue = opt.xValue || 0; 944 const xInverse = opt.xInverse || 0; 945 let isWindow = undefined; 946 if(isFunc(xFunc)){ 947 isWindow = false; 948 if(isFunc(xStep) || isFunc(xFinal)){ 949 toss3("Ambiguous arguments: scalar or aggregate?"); 950 } 951 xStep = xFinal = null; 952 }else if(isFunc(xStep)){ 953 if(!isFunc(xFinal)){ 954 toss3("Missing xFinal() callback for aggregate or window UDF."); 955 } 956 xFunc = null; 957 }else if(isFunc(xFinal)){ 958 toss3("Missing xStep() callback for aggregate or window UDF."); 959 }else{ 960 toss3("Missing function-type properties."); 961 } 962 if(false === isWindow){ 963 if(isFunc(xValue) || isFunc(xInverse)){ 964 toss3("xValue and xInverse are not permitted for non-window UDFs."); 965 } 966 }else if(isFunc(xValue)){ 967 if(!isFunc(xInverse)){ 968 toss3("xInverse must be provided if xValue is."); 969 } 970 isWindow = true; 971 }else if(isFunc(xInverse)){ 972 toss3("xValue must be provided if xInverse is."); 973 } 974 const pApp = opt.pApp; 975 if(undefined!==pApp && 976 null!==pApp && 977 (('number'!==typeof pApp) || !util.isInt32(pApp))){ 978 toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); 979 } 980 const xDestroy = opt.xDestroy || 0; 981 if(xDestroy && !isFunc(xDestroy)){ 982 toss3("xDestroy property must be a function."); 983 } 984 let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; 985 if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; 986 if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; 987 if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS; 988 name = name.toLowerCase(); 989 const xArity = xFunc || xStep; 990 const arity = getOwnOption(opt, 'arity'); 991 const arityArg = ('number'===typeof arity 992 ? arity 993 : (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0)); 994 let rc; 995 if( isWindow ){ 996 rc = capi.sqlite3_create_window_function( 997 this.pointer, name, arityArg, 998 capi.SQLITE_UTF8 | fFlags, pApp || 0, 999 xStep, xFinal, xValue, xInverse, xDestroy); 1000 }else{ 1001 rc = capi.sqlite3_create_function_v2( 1002 this.pointer, name, arityArg, 1003 capi.SQLITE_UTF8 | fFlags, pApp || 0, 1004 xFunc, xStep, xFinal, xDestroy); 1005 } 1006 DB.checkRc(this, rc); 1007 return this; 1008 }/*createFunction()*/, 1009 /** 1010 Prepares the given SQL, step()s it one time, and returns 1011 the value of the first result column. If it has no results, 1012 undefined is returned. 1013 1014 If passed a second argument, it is treated like an argument 1015 to Stmt.bind(), so may be any type supported by that 1016 function. Passing the undefined value is the same as passing 1017 no value, which is useful when... 1018 1019 If passed a 3rd argument, it is expected to be one of the 1020 SQLITE_{typename} constants. Passing the undefined value is 1021 the same as not passing a value. 1022 1023 Throws on error (e.g. malformed SQL). 1024 */ 1025 selectValue: function(sql,bind,asType){ 1026 let stmt, rc; 1027 try { 1028 stmt = this.prepare(sql).bind(bind); 1029 if(stmt.step()) rc = stmt.get(0,asType); 1030 }finally{ 1031 if(stmt) stmt.finalize(); 1032 } 1033 return rc; 1034 }, 1035 /** 1036 Prepares the given SQL, step()s it one time, and returns an 1037 array containing the values of the first result row. If it has 1038 no results, `undefined` is returned. 1039 1040 If passed a second argument other than `undefined`, it is 1041 treated like an argument to Stmt.bind(), so may be any type 1042 supported by that function. 1043 1044 Throws on error (e.g. malformed SQL). 1045 */ 1046 selectArray: function(sql,bind){ 1047 return __selectFirstRow(this, sql, bind, []); 1048 }, 1049 1050 /** 1051 Prepares the given SQL, step()s it one time, and returns an 1052 object containing the key/value pairs of the first result 1053 row. If it has no results, `undefined` is returned. 1054 1055 Note that the order of returned object's keys is not guaranteed 1056 to be the same as the order of the fields in the query string. 1057 1058 If passed a second argument other than `undefined`, it is 1059 treated like an argument to Stmt.bind(), so may be any type 1060 supported by that function. 1061 1062 Throws on error (e.g. malformed SQL). 1063 */ 1064 selectObject: function(sql,bind){ 1065 return __selectFirstRow(this, sql, bind, {}); 1066 }, 1067 1068 /** 1069 Returns the number of currently-opened Stmt handles for this db 1070 handle, or 0 if this DB instance is closed. 1071 */ 1072 openStatementCount: function(){ 1073 return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; 1074 }, 1075 1076 /** 1077 Starts a transaction, calls the given callback, and then either 1078 rolls back or commits the savepoint, depending on whether the 1079 callback throws. The callback is passed this db object as its 1080 only argument. On success, returns the result of the 1081 callback. Throws on error. 1082 1083 Note that transactions may not be nested, so this will throw if 1084 it is called recursively. For nested transactions, use the 1085 savepoint() method or manually manage SAVEPOINTs using exec(). 1086 */ 1087 transaction: function(callback){ 1088 affirmDbOpen(this).exec("BEGIN"); 1089 try { 1090 const rc = callback(this); 1091 this.exec("COMMIT"); 1092 return rc; 1093 }catch(e){ 1094 this.exec("ROLLBACK"); 1095 throw e; 1096 } 1097 }, 1098 1099 /** 1100 This works similarly to transaction() but uses sqlite3's SAVEPOINT 1101 feature. This function starts a savepoint (with an unspecified name) 1102 and calls the given callback function, passing it this db object. 1103 If the callback returns, the savepoint is released (committed). If 1104 the callback throws, the savepoint is rolled back. If it does not 1105 throw, it returns the result of the callback. 1106 */ 1107 savepoint: function(callback){ 1108 affirmDbOpen(this).exec("SAVEPOINT oo1"); 1109 try { 1110 const rc = callback(this); 1111 this.exec("RELEASE oo1"); 1112 return rc; 1113 }catch(e){ 1114 this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); 1115 throw e; 1116 } 1117 } 1118 }/*DB.prototype*/; 1119 1120 1121 /** Throws if the given Stmt has been finalized, else stmt is 1122 returned. */ 1123 const affirmStmtOpen = function(stmt){ 1124 if(!stmt.pointer) toss3("Stmt has been closed."); 1125 return stmt; 1126 }; 1127 1128 /** Returns an opaque truthy value from the BindTypes 1129 enum if v's type is a valid bindable type, else 1130 returns a falsy value. As a special case, a value of 1131 undefined is treated as a bind type of null. */ 1132 const isSupportedBindType = function(v){ 1133 let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v]; 1134 switch(t){ 1135 case BindTypes.boolean: 1136 case BindTypes.null: 1137 case BindTypes.number: 1138 case BindTypes.string: 1139 return t; 1140 case BindTypes.bigint: 1141 if(wasm.bigIntEnabled) return t; 1142 /* else fall through */ 1143 default: 1144 //console.log("isSupportedBindType",t,v); 1145 return util.isBindableTypedArray(v) ? BindTypes.blob : undefined; 1146 } 1147 }; 1148 1149 /** 1150 If isSupportedBindType(v) returns a truthy value, this 1151 function returns that value, else it throws. 1152 */ 1153 const affirmSupportedBindType = function(v){ 1154 //console.log('affirmSupportedBindType',v); 1155 return isSupportedBindType(v) || toss3("Unsupported bind() argument type:",typeof v); 1156 }; 1157 1158 /** 1159 If key is a number and within range of stmt's bound parameter 1160 count, key is returned. 1161 1162 If key is not a number then it is checked against named 1163 parameters. If a match is found, its index is returned. 1164 1165 Else it throws. 1166 */ 1167 const affirmParamIndex = function(stmt,key){ 1168 const n = ('number'===typeof key) 1169 ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); 1170 if(0===n || !util.isInt32(n)){ 1171 toss3("Invalid bind() parameter name: "+key); 1172 } 1173 else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range."); 1174 return n; 1175 }; 1176 1177 /** 1178 If stmt._isLocked is truthy, this throws an exception 1179 complaining that the 2nd argument (an operation name, 1180 e.g. "bind()") is not legal while the statement is "locked". 1181 Locking happens before an exec()-like callback is passed a 1182 statement, to ensure that the callback does not mutate or 1183 finalize the statement. If it does not throw, it returns stmt. 1184 */ 1185 const affirmUnlocked = function(stmt,currentOpName){ 1186 if(stmt._isLocked){ 1187 toss3("Operation is illegal when statement is locked:",currentOpName); 1188 } 1189 return stmt; 1190 }; 1191 1192 /** 1193 Binds a single bound parameter value on the given stmt at the 1194 given index (numeric or named) using the given bindType (see 1195 the BindTypes enum) and value. Throws on error. Returns stmt on 1196 success. 1197 */ 1198 const bindOne = function f(stmt,ndx,bindType,val){ 1199 affirmUnlocked(stmt, 'bind()'); 1200 if(!f._){ 1201 f._tooBigInt = (v)=>toss3( 1202 "BigInt value is too big to store without precision loss:", v 1203 ); 1204 /* Reminder: when not in BigInt mode, it's impossible for 1205 JS to represent a number out of the range we can bind, 1206 so we have no range checking. */ 1207 f._ = { 1208 string: function(stmt, ndx, val, asBlob){ 1209 if(1){ 1210 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1211 const stack = wasm.scopedAllocPush(); 1212 try{ 1213 const n = wasm.jstrlen(val); 1214 const pStr = wasm.scopedAlloc(n); 1215 wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false); 1216 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1217 return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); 1218 }finally{ 1219 wasm.scopedAllocPop(stack); 1220 } 1221 }else{ 1222 const bytes = wasm.jstrToUintArray(val,false); 1223 const pStr = wasm.alloc(bytes.length || 1); 1224 wasm.heap8u().set(bytes.length ? bytes : [0], pStr); 1225 try{ 1226 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1227 return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); 1228 }finally{ 1229 wasm.dealloc(pStr); 1230 } 1231 } 1232 } 1233 }; 1234 }/* static init */ 1235 affirmSupportedBindType(val); 1236 ndx = affirmParamIndex(stmt,ndx); 1237 let rc = 0; 1238 switch((null===val || undefined===val) ? BindTypes.null : bindType){ 1239 case BindTypes.null: 1240 rc = capi.sqlite3_bind_null(stmt.pointer, ndx); 1241 break; 1242 case BindTypes.string: 1243 rc = f._.string(stmt, ndx, val, false); 1244 break; 1245 case BindTypes.number: { 1246 let m; 1247 if(util.isInt32(val)) m = capi.sqlite3_bind_int; 1248 else if('bigint'===typeof val){ 1249 if(!util.bigIntFits64(val)){ 1250 f._tooBigInt(val); 1251 }else if(wasm.bigIntEnabled){ 1252 m = capi.sqlite3_bind_int64; 1253 }else if(util.bigIntFitsDouble(val)){ 1254 val = Number(val); 1255 m = capi.sqlite3_bind_double; 1256 }else{ 1257 f._tooBigInt(val); 1258 } 1259 }else{ // !int32, !bigint 1260 val = Number(val); 1261 if(wasm.bigIntEnabled && Number.isInteger(val)){ 1262 m = capi.sqlite3_bind_int64; 1263 }else{ 1264 m = capi.sqlite3_bind_double; 1265 } 1266 } 1267 rc = m(stmt.pointer, ndx, val); 1268 break; 1269 } 1270 case BindTypes.boolean: 1271 rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); 1272 break; 1273 case BindTypes.blob: { 1274 if('string'===typeof val){ 1275 rc = f._.string(stmt, ndx, val, true); 1276 }else if(!util.isBindableTypedArray(val)){ 1277 toss3("Binding a value as a blob requires", 1278 "that it be a string, Uint8Array, or Int8Array."); 1279 }else if(1){ 1280 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1281 const stack = wasm.scopedAllocPush(); 1282 try{ 1283 const pBlob = wasm.scopedAlloc(val.byteLength || 1); 1284 wasm.heap8().set(val.byteLength ? val : [0], pBlob) 1285 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1286 capi.SQLITE_TRANSIENT); 1287 }finally{ 1288 wasm.scopedAllocPop(stack); 1289 } 1290 }else{ 1291 const pBlob = wasm.allocFromTypedArray(val); 1292 try{ 1293 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1294 capi.SQLITE_TRANSIENT); 1295 }finally{ 1296 wasm.dealloc(pBlob); 1297 } 1298 } 1299 break; 1300 } 1301 default: 1302 console.warn("Unsupported bind() argument type:",val); 1303 toss3("Unsupported bind() argument type: "+(typeof val)); 1304 } 1305 if(rc) DB.checkRc(stmt.db.pointer, rc); 1306 return stmt; 1307 }; 1308 1309 Stmt.prototype = { 1310 /** 1311 "Finalizes" this statement. This is a no-op if the 1312 statement has already been finalizes. Returns 1313 undefined. Most methods in this class will throw if called 1314 after this is. 1315 */ 1316 finalize: function(){ 1317 if(this.pointer){ 1318 affirmUnlocked(this,'finalize()'); 1319 delete __stmtMap.get(this.db)[this.pointer]; 1320 capi.sqlite3_finalize(this.pointer); 1321 __ptrMap.delete(this); 1322 delete this._mayGet; 1323 delete this.columnCount; 1324 delete this.parameterCount; 1325 delete this.db; 1326 delete this._isLocked; 1327 } 1328 }, 1329 /** Clears all bound values. Returns this object. 1330 Throws if this statement has been finalized. */ 1331 clearBindings: function(){ 1332 affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') 1333 capi.sqlite3_clear_bindings(this.pointer); 1334 this._mayGet = false; 1335 return this; 1336 }, 1337 /** 1338 Resets this statement so that it may be step()ed again 1339 from the beginning. Returns this object. Throws if this 1340 statement has been finalized. 1341 1342 If passed a truthy argument then this.clearBindings() is 1343 also called, otherwise any existing bindings, along with 1344 any memory allocated for them, are retained. 1345 */ 1346 reset: function(alsoClearBinds){ 1347 affirmUnlocked(this,'reset()'); 1348 if(alsoClearBinds) this.clearBindings(); 1349 capi.sqlite3_reset(affirmStmtOpen(this).pointer); 1350 this._mayGet = false; 1351 return this; 1352 }, 1353 /** 1354 Binds one or more values to its bindable parameters. It 1355 accepts 1 or 2 arguments: 1356 1357 If passed a single argument, it must be either an array, an 1358 object, or a value of a bindable type (see below). 1359 1360 If passed 2 arguments, the first one is the 1-based bind 1361 index or bindable parameter name and the second one must be 1362 a value of a bindable type. 1363 1364 Bindable value types: 1365 1366 - null is bound as NULL. 1367 1368 - undefined as a standalone value is a no-op intended to 1369 simplify certain client-side use cases: passing undefined as 1370 a value to this function will not actually bind anything and 1371 this function will skip confirmation that binding is even 1372 legal. (Those semantics simplify certain client-side uses.) 1373 Conversely, a value of undefined as an array or object 1374 property when binding an array/object (see below) is treated 1375 the same as null. 1376 1377 - Numbers are bound as either doubles or integers: doubles if 1378 they are larger than 32 bits, else double or int32, depending 1379 on whether they have a fractional part. Booleans are bound as 1380 integer 0 or 1. It is not expected the distinction of binding 1381 doubles which have no fractional parts is integers is 1382 significant for the majority of clients due to sqlite3's data 1383 typing model. If [BigInt] support is enabled then this 1384 routine will bind BigInt values as 64-bit integers if they'll 1385 fit in 64 bits. If that support disabled, it will store the 1386 BigInt as an int32 or a double if it can do so without loss 1387 of precision. If the BigInt is _too BigInt_ then it will 1388 throw. 1389 1390 - Strings are bound as strings (use bindAsBlob() to force 1391 blob binding). 1392 1393 - Uint8Array and Int8Array instances are bound as blobs. 1394 (TODO: binding the other TypedArray types.) 1395 1396 If passed an array, each element of the array is bound at 1397 the parameter index equal to the array index plus 1 1398 (because arrays are 0-based but binding is 1-based). 1399 1400 If passed an object, each object key is treated as a 1401 bindable parameter name. The object keys _must_ match any 1402 bindable parameter names, including any `$`, `@`, or `:` 1403 prefix. Because `$` is a legal identifier chararacter in 1404 JavaScript, that is the suggested prefix for bindable 1405 parameters: `stmt.bind({$a: 1, $b: 2})`. 1406 1407 It returns this object on success and throws on 1408 error. Errors include: 1409 1410 - Any bind index is out of range, a named bind parameter 1411 does not match, or this statement has no bindable 1412 parameters. 1413 1414 - Any value to bind is of an unsupported type. 1415 1416 - Passed no arguments or more than two. 1417 1418 - The statement has been finalized. 1419 */ 1420 bind: function(/*[ndx,] arg*/){ 1421 affirmStmtOpen(this); 1422 let ndx, arg; 1423 switch(arguments.length){ 1424 case 1: ndx = 1; arg = arguments[0]; break; 1425 case 2: ndx = arguments[0]; arg = arguments[1]; break; 1426 default: toss3("Invalid bind() arguments."); 1427 } 1428 if(undefined===arg){ 1429 /* It might seem intuitive to bind undefined as NULL 1430 but this approach simplifies certain client-side 1431 uses when passing on arguments between 2+ levels of 1432 functions. */ 1433 return this; 1434 }else if(!this.parameterCount){ 1435 toss3("This statement has no bindable parameters."); 1436 } 1437 this._mayGet = false; 1438 if(null===arg){ 1439 /* bind NULL */ 1440 return bindOne(this, ndx, BindTypes.null, arg); 1441 } 1442 else if(Array.isArray(arg)){ 1443 /* bind each entry by index */ 1444 if(1!==arguments.length){ 1445 toss3("When binding an array, an index argument is not permitted."); 1446 } 1447 arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); 1448 return this; 1449 } 1450 else if('object'===typeof arg/*null was checked above*/ 1451 && !util.isBindableTypedArray(arg)){ 1452 /* Treat each property of arg as a named bound parameter. */ 1453 if(1!==arguments.length){ 1454 toss3("When binding an object, an index argument is not permitted."); 1455 } 1456 Object.keys(arg) 1457 .forEach(k=>bindOne(this, k, 1458 affirmSupportedBindType(arg[k]), 1459 arg[k])); 1460 return this; 1461 }else{ 1462 return bindOne(this, ndx, affirmSupportedBindType(arg), arg); 1463 } 1464 toss3("Should not reach this point."); 1465 }, 1466 /** 1467 Special case of bind() which binds the given value using the 1468 BLOB binding mechanism instead of the default selected one for 1469 the value. The ndx may be a numbered or named bind index. The 1470 value must be of type string, null/undefined (both get treated 1471 as null), or a TypedArray of a type supported by the bind() 1472 API. 1473 1474 If passed a single argument, a bind index of 1 is assumed and 1475 the first argument is the value. 1476 */ 1477 bindAsBlob: function(ndx,arg){ 1478 affirmStmtOpen(this); 1479 if(1===arguments.length){ 1480 arg = ndx; 1481 ndx = 1; 1482 } 1483 const t = affirmSupportedBindType(arg); 1484 if(BindTypes.string !== t && BindTypes.blob !== t 1485 && BindTypes.null !== t){ 1486 toss3("Invalid value type for bindAsBlob()"); 1487 } 1488 bindOne(this, ndx, BindTypes.blob, arg); 1489 this._mayGet = false; 1490 return this; 1491 }, 1492 /** 1493 Steps the statement one time. If the result indicates that a 1494 row of data is available, a truthy value is returned. 1495 If no row of data is available, a falsy 1496 value is returned. Throws on error. 1497 */ 1498 step: function(){ 1499 affirmUnlocked(this, 'step()'); 1500 const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); 1501 switch(rc){ 1502 case capi.SQLITE_DONE: return this._mayGet = false; 1503 case capi.SQLITE_ROW: return this._mayGet = true; 1504 default: 1505 this._mayGet = false; 1506 console.warn("sqlite3_step() rc=",rc, 1507 capi.sqlite3_js_rc_str(rc), 1508 "SQL =", capi.sqlite3_sql(this.pointer)); 1509 DB.checkRc(this.db.pointer, rc); 1510 } 1511 }, 1512 /** 1513 Functions exactly like step() except that... 1514 1515 1) On success, it calls this.reset() and returns this object. 1516 2) On error, it throws and does not call reset(). 1517 1518 This is intended to simplify constructs like: 1519 1520 ``` 1521 for(...) { 1522 stmt.bind(...).stepReset(); 1523 } 1524 ``` 1525 1526 Note that the reset() call makes it illegal to call this.get() 1527 after the step. 1528 */ 1529 stepReset: function(){ 1530 this.step(); 1531 return this.reset(); 1532 }, 1533 /** 1534 Functions like step() except that it finalizes this statement 1535 immediately after stepping unless the step cannot be performed 1536 because the statement is locked. Throws on error, but any error 1537 other than the statement-is-locked case will also trigger 1538 finalization of this statement. 1539 1540 On success, it returns true if the step indicated that a row of 1541 data was available, else it returns false. 1542 1543 This is intended to simplify use cases such as: 1544 1545 ``` 1546 aDb.prepare("insert into foo(a) values(?)").bind(123).stepFinalize(); 1547 ``` 1548 */ 1549 stepFinalize: function(){ 1550 const rc = this.step(); 1551 this.finalize(); 1552 return rc; 1553 }, 1554 /** 1555 Fetches the value from the given 0-based column index of 1556 the current data row, throwing if index is out of range. 1557 1558 Requires that step() has just returned a truthy value, else 1559 an exception is thrown. 1560 1561 By default it will determine the data type of the result 1562 automatically. If passed a second arugment, it must be one 1563 of the enumeration values for sqlite3 types, which are 1564 defined as members of the sqlite3 module: SQLITE_INTEGER, 1565 SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, 1566 except for undefined, will trigger an exception. Passing 1567 undefined is the same as not passing a value. It is legal 1568 to, e.g., fetch an integer value as a string, in which case 1569 sqlite3 will convert the value to a string. 1570 1571 If ndx is an array, this function behaves a differently: it 1572 assigns the indexes of the array, from 0 to the number of 1573 result columns, to the values of the corresponding column, 1574 and returns that array. 1575 1576 If ndx is a plain object, this function behaves even 1577 differentlier: it assigns the properties of the object to 1578 the values of their corresponding result columns. 1579 1580 Blobs are returned as Uint8Array instances. 1581 1582 Potential TODO: add type ID SQLITE_JSON, which fetches the 1583 result as a string and passes it (if it's not null) to 1584 JSON.parse(), returning the result of that. Until then, 1585 getJSON() can be used for that. 1586 */ 1587 get: function(ndx,asType){ 1588 if(!affirmStmtOpen(this)._mayGet){ 1589 toss3("Stmt.step() has not (recently) returned true."); 1590 } 1591 if(Array.isArray(ndx)){ 1592 let i = 0; 1593 while(i<this.columnCount){ 1594 ndx[i] = this.get(i++); 1595 } 1596 return ndx; 1597 }else if(ndx && 'object'===typeof ndx){ 1598 let i = 0; 1599 while(i<this.columnCount){ 1600 ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++); 1601 } 1602 return ndx; 1603 } 1604 affirmColIndex(this, ndx); 1605 switch(undefined===asType 1606 ? capi.sqlite3_column_type(this.pointer, ndx) 1607 : asType){ 1608 case capi.SQLITE_NULL: return null; 1609 case capi.SQLITE_INTEGER:{ 1610 if(wasm.bigIntEnabled){ 1611 const rc = capi.sqlite3_column_int64(this.pointer, ndx); 1612 if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ 1613 /* Coerce "normal" number ranges to normal number values, 1614 and only return BigInt-type values for numbers out of this 1615 range. */ 1616 return Number(rc).valueOf(); 1617 } 1618 return rc; 1619 }else{ 1620 const rc = capi.sqlite3_column_double(this.pointer, ndx); 1621 if(rc>Number.MAX_SAFE_INTEGER || rc<Number.MIN_SAFE_INTEGER){ 1622 /* Throwing here is arguable but, since we're explicitly 1623 extracting an SQLITE_INTEGER-type value, it seems fair to throw 1624 if the extracted number is out of range for that type. 1625 This policy may be laxened to simply pass on the number and 1626 hope for the best, as the C API would do. */ 1627 toss3("Integer is out of range for JS integer range: "+rc); 1628 } 1629 //console.log("get integer rc=",rc,isInt32(rc)); 1630 return util.isInt32(rc) ? (rc | 0) : rc; 1631 } 1632 } 1633 case capi.SQLITE_FLOAT: 1634 return capi.sqlite3_column_double(this.pointer, ndx); 1635 case capi.SQLITE_TEXT: 1636 return capi.sqlite3_column_text(this.pointer, ndx); 1637 case capi.SQLITE_BLOB: { 1638 const n = capi.sqlite3_column_bytes(this.pointer, ndx), 1639 ptr = capi.sqlite3_column_blob(this.pointer, ndx), 1640 rc = new Uint8Array(n); 1641 //heap = n ? wasm.heap8() : false; 1642 if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0); 1643 //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; 1644 if(n && this.db._blobXfer instanceof Array){ 1645 /* This is an optimization soley for the 1646 Worker-based API. These values will be 1647 transfered to the main thread directly 1648 instead of being copied. */ 1649 this.db._blobXfer.push(rc.buffer); 1650 } 1651 return rc; 1652 } 1653 default: toss3("Don't know how to translate", 1654 "type of result column #"+ndx+"."); 1655 } 1656 toss3("Not reached."); 1657 }, 1658 /** Equivalent to get(ndx) but coerces the result to an 1659 integer. */ 1660 getInt: function(ndx){return this.get(ndx,capi.SQLITE_INTEGER)}, 1661 /** Equivalent to get(ndx) but coerces the result to a 1662 float. */ 1663 getFloat: function(ndx){return this.get(ndx,capi.SQLITE_FLOAT)}, 1664 /** Equivalent to get(ndx) but coerces the result to a 1665 string. */ 1666 getString: function(ndx){return this.get(ndx,capi.SQLITE_TEXT)}, 1667 /** Equivalent to get(ndx) but coerces the result to a 1668 Uint8Array. */ 1669 getBlob: function(ndx){return this.get(ndx,capi.SQLITE_BLOB)}, 1670 /** 1671 A convenience wrapper around get() which fetches the value 1672 as a string and then, if it is not null, passes it to 1673 JSON.parse(), returning that result. Throws if parsing 1674 fails. If the result is null, null is returned. An empty 1675 string, on the other hand, will trigger an exception. 1676 */ 1677 getJSON: function(ndx){ 1678 const s = this.get(ndx, capi.SQLITE_STRING); 1679 return null===s ? s : JSON.parse(s); 1680 }, 1681 // Design note: the only reason most of these getters have a 'get' 1682 // prefix is for consistency with getVALUE_TYPE(). The latter 1683 // arguably really need that prefix for API readability and the 1684 // rest arguably don't, but consistency is a powerful thing. 1685 /** 1686 Returns the result column name of the given index, or 1687 throws if index is out of bounds or this statement has been 1688 finalized. This can be used without having run step() 1689 first. 1690 */ 1691 getColumnName: function(ndx){ 1692 return capi.sqlite3_column_name( 1693 affirmColIndex(affirmStmtOpen(this),ndx).pointer, ndx 1694 ); 1695 }, 1696 /** 1697 If this statement potentially has result columns, this 1698 function returns an array of all such names. If passed an 1699 array, it is used as the target and all names are appended 1700 to it. Returns the target array. Throws if this statement 1701 cannot have result columns. This object's columnCount member 1702 holds the number of columns. 1703 */ 1704 getColumnNames: function(tgt=[]){ 1705 affirmColIndex(affirmStmtOpen(this),0); 1706 for(let i = 0; i < this.columnCount; ++i){ 1707 tgt.push(capi.sqlite3_column_name(this.pointer, i)); 1708 } 1709 return tgt; 1710 }, 1711 /** 1712 If this statement has named bindable parameters and the 1713 given name matches one, its 1-based bind index is 1714 returned. If no match is found, 0 is returned. If it has no 1715 bindable parameters, the undefined value is returned. 1716 */ 1717 getParamIndex: function(name){ 1718 return (affirmStmtOpen(this).parameterCount 1719 ? capi.sqlite3_bind_parameter_index(this.pointer, name) 1720 : undefined); 1721 } 1722 }/*Stmt.prototype*/; 1723 1724 {/* Add the `pointer` property to DB and Stmt. */ 1725 const prop = { 1726 enumerable: true, 1727 get: function(){return __ptrMap.get(this)}, 1728 set: ()=>toss3("The pointer property is read-only.") 1729 } 1730 Object.defineProperty(Stmt.prototype, 'pointer', prop); 1731 Object.defineProperty(DB.prototype, 'pointer', prop); 1732 } 1733 1734 /** The OO API's public namespace. */ 1735 sqlite3.oo1 = { 1736 version: { 1737 lib: capi.sqlite3_libversion(), 1738 ooApi: "0.1" 1739 }, 1740 DB, 1741 Stmt 1742 }/*oo1 object*/; 1743 1744 if(util.isUIThread()){ 1745 /** 1746 Functionally equivalent to DB(storageName,'c','kvvfs') except 1747 that it throws if the given storage name is not one of 'local' 1748 or 'session'. 1749 */ 1750 sqlite3.oo1.JsStorageDb = function(storageName='session'){ 1751 if('session'!==storageName && 'local'!==storageName){ 1752 toss3("JsStorageDb db name must be one of 'session' or 'local'."); 1753 } 1754 dbCtorHelper.call(this, { 1755 filename: storageName, 1756 flags: 'c', 1757 vfs: "kvvfs" 1758 }); 1759 }; 1760 const jdb = sqlite3.oo1.JsStorageDb; 1761 jdb.prototype = Object.create(DB.prototype); 1762 /** Equivalent to sqlite3_js_kvvfs_clear(). */ 1763 jdb.clearStorage = capi.sqlite3_js_kvvfs_clear; 1764 /** 1765 Clears this database instance's storage or throws if this 1766 instance has been closed. Returns the number of 1767 database blocks which were cleaned up. 1768 */ 1769 jdb.prototype.clearStorage = function(){ 1770 return jdb.clearStorage(affirmDbOpen(this).filename); 1771 }; 1772 /** Equivalent to sqlite3_js_kvvfs_size(). */ 1773 jdb.storageSize = capi.sqlite3_js_kvvfs_size; 1774 /** 1775 Returns the _approximate_ number of bytes this database takes 1776 up in its storage or throws if this instance has been closed. 1777 */ 1778 jdb.prototype.storageSize = function(){ 1779 return jdb.storageSize(affirmDbOpen(this).filename); 1780 }; 1781 }/*main-window-only bits*/ 1782 1783}); 1784 1785