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