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