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 /** 430 Finalizes all open statements and closes this database 431 connection. This is a no-op if the db has already been 432 closed. After calling close(), `this.pointer` will resolve to 433 `undefined`, so that can be used to check whether the db 434 instance is still opened. 435 436 If this.onclose.before is a function then it is called before 437 any close-related cleanup. 438 439 If this.onclose.after is a function then it is called after the 440 db is closed but before auxiliary state like this.filename is 441 cleared. 442 443 Both onclose handlers are passed this object. If this db is not 444 opened, neither of the handlers are called. Any exceptions the 445 handlers throw are ignored because "destructors must not 446 throw." 447 448 Note that garbage collection of a db handle, if it happens at 449 all, will never trigger close(), so onclose handlers are not a 450 reliable way to implement close-time cleanup or maintenance of 451 a db. 452 */ 453 close: function(){ 454 if(this.pointer){ 455 if(this.onclose && (this.onclose.before instanceof Function)){ 456 try{this.onclose.before(this)} 457 catch(e){/*ignore*/} 458 } 459 const pDb = this.pointer; 460 Object.keys(__stmtMap.get(this)).forEach((k,s)=>{ 461 if(s && s.pointer) s.finalize(); 462 }); 463 Object.values(__udfMap.get(this)).forEach( 464 capi.wasm.uninstallFunction.bind(capi.wasm) 465 ); 466 __ptrMap.delete(this); 467 __stmtMap.delete(this); 468 __udfMap.delete(this); 469 capi.sqlite3_close_v2(pDb); 470 if(this.onclose && (this.onclose.after instanceof Function)){ 471 try{this.onclose.after(this)} 472 catch(e){/*ignore*/} 473 } 474 delete this.filename; 475 } 476 }, 477 /** 478 Returns the number of changes, as per sqlite3_changes() 479 (if the first argument is false) or sqlite3_total_changes() 480 (if it's true). If the 2nd argument is true, it uses 481 sqlite3_changes64() or sqlite3_total_changes64(), which 482 will trigger an exception if this build does not have 483 BigInt support enabled. 484 */ 485 changes: function(total=false,sixtyFour=false){ 486 const p = affirmDbOpen(this).pointer; 487 if(total){ 488 return sixtyFour 489 ? capi.sqlite3_total_changes64(p) 490 : capi.sqlite3_total_changes(p); 491 }else{ 492 return sixtyFour 493 ? capi.sqlite3_changes64(p) 494 : capi.sqlite3_changes(p); 495 } 496 }, 497 /** 498 Similar to the this.filename property but will return a falsy 499 value for special names like ":memory:". Throws if the DB has 500 been closed. If passed an argument it then it will return the 501 filename of the ATTACHEd db with that name, else it assumes a 502 name of `main`. The argument may be either a JS string or 503 a pointer to a WASM-allocated C-string. 504 */ 505 getFilename: function(dbName='main'){ 506 return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); 507 }, 508 /** 509 Returns true if this db instance has a name which resolves to a 510 file. If the name is "" or starts with ":", it resolves to false. 511 Note that it is not aware of the peculiarities of URI-style 512 names and a URI-style name for a ":memory:" db will fool it. 513 Returns false if this db is closed. 514 */ 515 hasFilename: function(){ 516 return this.filename && ':'!==this.filename[0]; 517 }, 518 /** 519 Returns the name of the given 0-based db number, as documented 520 for sqlite3_db_name(). 521 */ 522 dbName: function(dbNumber=0){ 523 return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); 524 }, 525 /** 526 Compiles the given SQL and returns a prepared Stmt. This is 527 the only way to create new Stmt objects. Throws on error. 528 529 The given SQL must be a string, a Uint8Array holding SQL, a 530 WASM pointer to memory holding the NUL-terminated SQL string, 531 or an array of strings. In the latter case, the array is 532 concatenated together, with no separators, to form the SQL 533 string (arrays are often a convenient way to formulate long 534 statements). If the SQL contains no statements, an 535 SQLite3Error is thrown. 536 537 Design note: the C API permits empty SQL, reporting it as a 0 538 result code and a NULL stmt pointer. Supporting that case here 539 would cause extra work for all clients: any use of the Stmt API 540 on such a statement will necessarily throw, so clients would be 541 required to check `stmt.pointer` after calling `prepare()` in 542 order to determine whether the Stmt instance is empty or not. 543 Long-time practice (with other sqlite3 script bindings) 544 suggests that the empty-prepare case is sufficiently rare that 545 supporting it here would simply hurt overall usability. 546 */ 547 prepare: function(sql){ 548 affirmDbOpen(this); 549 if(Array.isArray(sql)) sql = sql.join(''); 550 const stack = capi.wasm.scopedAllocPush(); 551 let ppStmt, pStmt; 552 try{ 553 ppStmt = capi.wasm.scopedAllocPtr()/* output (sqlite3_stmt**) arg */; 554 DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); 555 pStmt = capi.wasm.getPtrValue(ppStmt); 556 } 557 finally {capi.wasm.scopedAllocPop(stack)} 558 if(!pStmt) toss3("Cannot prepare empty SQL."); 559 const stmt = new Stmt(this, pStmt, BindTypes); 560 __stmtMap.get(this)[pStmt] = stmt; 561 return stmt; 562 }, 563 /** 564 Executes one or more SQL statements in the form of a single 565 string. Its arguments must be either (sql,optionsObject) or 566 (optionsObject). In the latter case, optionsObject.sql 567 must contain the SQL to execute. Returns this 568 object. Throws on error. 569 570 If no SQL is provided, or a non-string is provided, an 571 exception is triggered. Empty SQL, on the other hand, is 572 simply a no-op. 573 574 The optional options object may contain any of the following 575 properties: 576 577 - `.sql` = the SQL to run (unless it's provided as the first 578 argument). This must be of type string, Uint8Array, or an array 579 of strings. In the latter case they're concatenated together 580 as-is, _with no separator_ between elements, before evaluation. 581 The array form is often simpler for long hand-written queries. 582 583 - `.bind` = a single value valid as an argument for 584 Stmt.bind(). This is _only_ applied to the _first_ non-empty 585 statement in the SQL which has any bindable parameters. (Empty 586 statements are skipped entirely.) 587 588 - `.saveSql` = an optional array. If set, the SQL of each 589 executed statement is appended to this array before the 590 statement is executed (but after it is prepared - we don't have 591 the string until after that). Empty SQL statements are elided. 592 593 ================================================================== 594 The following options apply _only_ to the _first_ statement 595 which has a non-zero result column count, regardless of whether 596 the statement actually produces any result rows. 597 ================================================================== 598 599 - `.columnNames`: if this is an array, the column names of the 600 result set are stored in this array before the callback (if 601 any) is triggered (regardless of whether the query produces any 602 result rows). If no statement has result columns, this value is 603 unchanged. Achtung: an SQL result may have multiple columns 604 with identical names. 605 606 - `.callback` = a function which gets called for each row of 607 the result set, but only if that statement has any result 608 _rows_. The callback's "this" is the options object, noting 609 that this function synthesizes one if the caller does not pass 610 one to exec(). The second argument passed to the callback is 611 always the current Stmt object, as it's needed if the caller 612 wants to fetch the column names or some such (noting that they 613 could also be fetched via `this.columnNames`, if the client 614 provides the `columnNames` option). 615 616 ACHTUNG: The callback MUST NOT modify the Stmt object. Calling 617 any of the Stmt.get() variants, Stmt.getColumnName(), or 618 similar, is legal, but calling step() or finalize() is 619 not. Member methods which are illegal in this context will 620 trigger an exception. 621 622 The first argument passed to the callback defaults to an array of 623 values from the current result row but may be changed with ... 624 625 - `.rowMode` = specifies the type of he callback's first argument. 626 It may be any of... 627 628 A) A string describing what type of argument should be passed 629 as the first argument to the callback: 630 631 A.1) `'array'` (the default) causes the results of 632 `stmt.get([])` to be passed to the `callback` and/or appended 633 to `resultRows`. 634 635 A.2) `'object'` causes the results of 636 `stmt.get(Object.create(null))` to be passed to the 637 `callback` and/or appended to `resultRows`. Achtung: an SQL 638 result may have multiple columns with identical names. In 639 that case, the right-most column will be the one set in this 640 object! 641 642 A.3) `'stmt'` causes the current Stmt to be passed to the 643 callback, but this mode will trigger an exception if 644 `resultRows` is an array because appending the statement to 645 the array would be downright unhelpful. 646 647 B) An integer, indicating a zero-based column in the result 648 row. Only that one single value will be passed on. 649 650 C) A string with a minimum length of 2 and leading character of 651 ':', '$', or '@' will fetch the row as an object, extract that 652 one field, and pass that field's value to the callback. Note 653 that these keys are case-sensitive so must match the case used 654 in the SQL. e.g. `"select a A from t"` with a `rowMode` of 655 `'$A'` would work but `'$a'` would not. A reference to a column 656 not in the result set will trigger an exception on the first 657 row (as the check is not performed until rows are fetched). 658 Note also that `$` is a legal identifier character in JS so 659 need not be quoted. (Design note: those 3 characters were 660 chosen because they are the characters support for naming bound 661 parameters.) 662 663 Any other `rowMode` value triggers an exception. 664 665 - `.resultRows`: if this is an array, it functions similarly to 666 the `callback` option: each row of the result set (if any), 667 with the exception that the `rowMode` 'stmt' is not legal. It 668 is legal to use both `resultRows` and `callback`, but 669 `resultRows` is likely much simpler to use for small data sets 670 and can be used over a WebWorker-style message interface. 671 exec() throws if `resultRows` is set and `rowMode` is 'stmt'. 672 673 674 Potential TODOs: 675 676 - `.bind`: permit an array of arrays/objects to bind. The first 677 sub-array would act on the first statement which has bindable 678 parameters (as it does now). The 2nd would act on the next such 679 statement, etc. 680 681 - `.callback` and `.resultRows`: permit an array entries with 682 semantics similar to those described for `.bind` above. 683 684 */ 685 exec: function(/*(sql [,obj]) || (obj)*/){ 686 affirmDbOpen(this); 687 const wasm = capi.wasm; 688 const arg = parseExecArgs(arguments); 689 if(!arg.sql){ 690 return (''===arg.sql) ? this : toss3("exec() requires an SQL string."); 691 } 692 const opt = arg.opt; 693 const callback = opt.callback; 694 let resultRows = (Array.isArray(opt.resultRows) 695 ? opt.resultRows : undefined); 696 let stmt; 697 let bind = opt.bind; 698 let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; 699 const stack = wasm.scopedAllocPush(); 700 try{ 701 const isTA = util.isSQLableTypedArray(arg.sql) 702 /* Optimization: if the SQL is a TypedArray we can save some string 703 conversion costs. */; 704 /* Allocate the two output pointers (ppStmt, pzTail) and heap 705 space for the SQL (pSql). When prepare_v2() returns, pzTail 706 will point to somewhere in pSql. */ 707 let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); 708 const ppStmt = wasm.scopedAlloc(/* output (sqlite3_stmt**) arg and pzTail */ 709 (2 * wasm.ptrSizeof) 710 + (sqlByteLen + 1/* SQL + NUL */)); 711 const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */; 712 let pSql = pzTail + wasm.ptrSizeof; 713 const pSqlEnd = pSql + sqlByteLen; 714 if(isTA) wasm.heap8().set(arg.sql, pSql); 715 else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); 716 wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/); 717 while(pSql && wasm.getMemValue(pSql, 'i8') 718 /* Maintenance reminder:^^^ _must_ be 'i8' or else we 719 will very likely cause an endless loop. What that's 720 doing is checking for a terminating NUL byte. If we 721 use i32 or similar then we read 4 bytes, read stuff 722 around the NUL terminator, and get stuck in and 723 endless loop at the end of the SQL, endlessly 724 re-preparing an empty statement. */ ){ 725 wasm.setPtrValue(ppStmt, 0); 726 wasm.setPtrValue(pzTail, 0); 727 DB.checkRc(this, capi.sqlite3_prepare_v3( 728 this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail 729 )); 730 const pStmt = wasm.getPtrValue(ppStmt); 731 pSql = wasm.getPtrValue(pzTail); 732 sqlByteLen = pSqlEnd - pSql; 733 if(!pStmt) continue; 734 if(Array.isArray(opt.saveSql)){ 735 opt.saveSql.push(capi.sqlite3_sql(pStmt).trim()); 736 } 737 stmt = new Stmt(this, pStmt, BindTypes); 738 if(bind && stmt.parameterCount){ 739 stmt.bind(bind); 740 bind = null; 741 } 742 if(evalFirstResult && stmt.columnCount){ 743 /* Only forward SELECT results for the FIRST query 744 in the SQL which potentially has them. */ 745 evalFirstResult = false; 746 if(Array.isArray(opt.columnNames)){ 747 stmt.getColumnNames(opt.columnNames); 748 } 749 while(!!arg.cbArg && stmt.step()){ 750 stmt._isLocked = true; 751 const row = arg.cbArg(stmt); 752 if(resultRows) resultRows.push(row); 753 if(callback) callback.apply(opt,[row,stmt]); 754 stmt._isLocked = false; 755 } 756 }else{ 757 stmt.step(); 758 } 759 stmt.finalize(); 760 stmt = null; 761 } 762 }/*catch(e){ 763 console.warn("DB.exec() is propagating exception",opt,e); 764 throw e; 765 }*/finally{ 766 if(stmt){ 767 delete stmt._isLocked; 768 stmt.finalize(); 769 } 770 wasm.scopedAllocPop(stack); 771 } 772 return this; 773 }/*exec()*/, 774 /** 775 Creates a new scalar UDF (User-Defined Function) which is 776 accessible via SQL code. This function may be called in any 777 of the following forms: 778 779 - (name, function) 780 - (name, function, optionsObject) 781 - (name, optionsObject) 782 - (optionsObject) 783 784 In the final two cases, the function must be defined as the 785 'callback' property of the options object. In the final 786 case, the function's name must be the 'name' property. 787 788 This can only be used to create scalar functions, not 789 aggregate or window functions. UDFs cannot be removed from 790 a DB handle after they're added. 791 792 On success, returns this object. Throws on error. 793 794 When called from SQL, arguments to the UDF, and its result, 795 will be converted between JS and SQL with as much fidelity 796 as is feasible, triggering an exception if a type 797 conversion cannot be determined. Some freedom is afforded 798 to numeric conversions due to friction between the JS and C 799 worlds: integers which are larger than 32 bits will be 800 treated as doubles, as JS does not support 64-bit integers 801 and it is (as of this writing) illegal to use WASM 802 functions which take or return 64-bit integers from JS. 803 804 The optional options object may contain flags to modify how 805 the function is defined: 806 807 - .arity: the number of arguments which SQL calls to this 808 function expect or require. The default value is the 809 callback's length property (i.e. the number of declared 810 parameters it has). A value of -1 means that the function 811 is variadic and may accept any number of arguments, up to 812 sqlite3's compile-time limits. sqlite3 will enforce the 813 argument count if is zero or greater. 814 815 The following properties correspond to flags documented at: 816 817 https://sqlite.org/c3ref/create_function.html 818 819 - .deterministic = SQLITE_DETERMINISTIC 820 - .directOnly = SQLITE_DIRECTONLY 821 - .innocuous = SQLITE_INNOCUOUS 822 823 Maintenance reminder: the ability to add new 824 WASM-accessible functions to the runtime requires that the 825 WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` 826 flag. 827 */ 828 createFunction: function f(name, callback,opt){ 829 switch(arguments.length){ 830 case 1: /* (optionsObject) */ 831 opt = name; 832 name = opt.name; 833 callback = opt.callback; 834 break; 835 case 2: /* (name, callback|optionsObject) */ 836 if(!(callback instanceof Function)){ 837 opt = callback; 838 callback = opt.callback; 839 } 840 break; 841 default: break; 842 } 843 if(!opt) opt = {}; 844 if(!(callback instanceof Function)){ 845 toss3("Invalid arguments: expecting a callback function."); 846 }else if('string' !== typeof name){ 847 toss3("Invalid arguments: missing function name."); 848 } 849 if(!f._extractArgs){ 850 /* Static init */ 851 f._extractArgs = function(argc, pArgv){ 852 let i, pVal, valType, arg; 853 const tgt = []; 854 for(i = 0; i < argc; ++i){ 855 pVal = capi.wasm.getPtrValue(pArgv + (capi.wasm.ptrSizeof * i)); 856 /** 857 Curiously: despite ostensibly requiring 8-byte 858 alignment, the pArgv array is parcelled into chunks of 859 4 bytes (1 pointer each). The values those point to 860 have 8-byte alignment but the individual argv entries 861 do not. 862 */ 863 valType = capi.sqlite3_value_type(pVal); 864 switch(valType){ 865 case capi.SQLITE_INTEGER: 866 case capi.SQLITE_FLOAT: 867 arg = capi.sqlite3_value_double(pVal); 868 break; 869 case capi.SQLITE_TEXT: 870 arg = capi.sqlite3_value_text(pVal); 871 break; 872 case capi.SQLITE_BLOB:{ 873 const n = capi.sqlite3_value_bytes(pVal); 874 const pBlob = capi.sqlite3_value_blob(pVal); 875 arg = new Uint8Array(n); 876 let i; 877 const heap = n ? capi.wasm.heap8() : false; 878 for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i]; 879 break; 880 } 881 case capi.SQLITE_NULL: 882 arg = null; break; 883 default: 884 toss3("Unhandled sqlite3_value_type()",valType, 885 "is possibly indicative of incorrect", 886 "pointer size assumption."); 887 } 888 tgt.push(arg); 889 } 890 return tgt; 891 }/*_extractArgs()*/; 892 f._setResult = function(pCx, val){ 893 switch(typeof val) { 894 case 'boolean': 895 capi.sqlite3_result_int(pCx, val ? 1 : 0); 896 break; 897 case 'number': { 898 (util.isInt32(val) 899 ? capi.sqlite3_result_int 900 : capi.sqlite3_result_double)(pCx, val); 901 break; 902 } 903 case 'string': 904 capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT); 905 break; 906 case 'object': 907 if(null===val) { 908 capi.sqlite3_result_null(pCx); 909 break; 910 }else if(util.isBindableTypedArray(val)){ 911 const pBlob = capi.wasm.allocFromTypedArray(val); 912 capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, 913 capi.SQLITE_TRANSIENT); 914 capi.wasm.dealloc(pBlob); 915 break; 916 } 917 // else fall through 918 default: 919 toss3("Don't not how to handle this UDF result value:",val); 920 }; 921 }/*_setResult()*/; 922 }/*static init*/ 923 const wrapper = function(pCx, argc, pArgv){ 924 try{ 925 f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv))); 926 }catch(e){ 927 if(e instanceof capi.WasmAllocError){ 928 capi.sqlite3_result_error_nomem(pCx); 929 }else{ 930 capi.sqlite3_result_error(pCx, e.message, -1); 931 } 932 } 933 }; 934 const pUdf = capi.wasm.installFunction(wrapper, "v(iii)"); 935 let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; 936 if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; 937 if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; 938 if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS; 939 name = name.toLowerCase(); 940 try { 941 DB.checkRc(this, capi.sqlite3_create_function_v2( 942 this.pointer, name, 943 (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), 944 capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, 945 null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); 946 }catch(e){ 947 capi.wasm.uninstallFunction(pUdf); 948 throw e; 949 } 950 const udfMap = __udfMap.get(this); 951 if(udfMap[name]){ 952 try{capi.wasm.uninstallFunction(udfMap[name])} 953 catch(e){/*ignore*/} 954 } 955 udfMap[name] = pUdf; 956 return this; 957 }/*createFunction()*/, 958 /** 959 Prepares the given SQL, step()s it one time, and returns 960 the value of the first result column. If it has no results, 961 undefined is returned. 962 963 If passed a second argument, it is treated like an argument 964 to Stmt.bind(), so may be any type supported by that 965 function. Passing the undefined value is the same as passing 966 no value, which is useful when... 967 968 If passed a 3rd argument, it is expected to be one of the 969 SQLITE_{typename} constants. Passing the undefined value is 970 the same as not passing a value. 971 972 Throws on error (e.g. malformedSQL). 973 */ 974 selectValue: function(sql,bind,asType){ 975 let stmt, rc; 976 try { 977 stmt = this.prepare(sql).bind(bind); 978 if(stmt.step()) rc = stmt.get(0,asType); 979 }finally{ 980 if(stmt) stmt.finalize(); 981 } 982 return rc; 983 }, 984 985 /** 986 Returns the number of currently-opened Stmt handles for this db 987 handle, or 0 if this DB instance is closed. 988 */ 989 openStatementCount: function(){ 990 return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; 991 }, 992 993 /** 994 Starts a transaction, calls the given callback, and then either 995 rolls back or commits the savepoint, depending on whether the 996 callback throws. The callback is passed this db object as its 997 only argument. On success, returns the result of the 998 callback. Throws on error. 999 1000 Note that transactions may not be nested, so this will throw if 1001 it is called recursively. For nested transactions, use the 1002 savepoint() method or manually manage SAVEPOINTs using exec(). 1003 */ 1004 transaction: function(callback){ 1005 affirmDbOpen(this).exec("BEGIN"); 1006 try { 1007 const rc = callback(this); 1008 this.exec("COMMIT"); 1009 return rc; 1010 }catch(e){ 1011 this.exec("ROLLBACK"); 1012 throw e; 1013 } 1014 }, 1015 1016 /** 1017 This works similarly to transaction() but uses sqlite3's SAVEPOINT 1018 feature. This function starts a savepoint (with an unspecified name) 1019 and calls the given callback function, passing it this db object. 1020 If the callback returns, the savepoint is released (committed). If 1021 the callback throws, the savepoint is rolled back. If it does not 1022 throw, it returns the result of the callback. 1023 */ 1024 savepoint: function(callback){ 1025 affirmDbOpen(this).exec("SAVEPOINT oo1"); 1026 try { 1027 const rc = callback(this); 1028 this.exec("RELEASE oo1"); 1029 return rc; 1030 }catch(e){ 1031 this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); 1032 throw e; 1033 } 1034 } 1035 }/*DB.prototype*/; 1036 1037 1038 /** Throws if the given Stmt has been finalized, else stmt is 1039 returned. */ 1040 const affirmStmtOpen = function(stmt){ 1041 if(!stmt.pointer) toss3("Stmt has been closed."); 1042 return stmt; 1043 }; 1044 1045 /** Returns an opaque truthy value from the BindTypes 1046 enum if v's type is a valid bindable type, else 1047 returns a falsy value. As a special case, a value of 1048 undefined is treated as a bind type of null. */ 1049 const isSupportedBindType = function(v){ 1050 let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v]; 1051 switch(t){ 1052 case BindTypes.boolean: 1053 case BindTypes.null: 1054 case BindTypes.number: 1055 case BindTypes.string: 1056 return t; 1057 case BindTypes.bigint: 1058 if(capi.wasm.bigIntEnabled) return t; 1059 /* else fall through */ 1060 default: 1061 //console.log("isSupportedBindType",t,v); 1062 return util.isBindableTypedArray(v) ? BindTypes.blob : undefined; 1063 } 1064 }; 1065 1066 /** 1067 If isSupportedBindType(v) returns a truthy value, this 1068 function returns that value, else it throws. 1069 */ 1070 const affirmSupportedBindType = function(v){ 1071 //console.log('affirmSupportedBindType',v); 1072 return isSupportedBindType(v) || toss3("Unsupported bind() argument type:",typeof v); 1073 }; 1074 1075 /** 1076 If key is a number and within range of stmt's bound parameter 1077 count, key is returned. 1078 1079 If key is not a number then it is checked against named 1080 parameters. If a match is found, its index is returned. 1081 1082 Else it throws. 1083 */ 1084 const affirmParamIndex = function(stmt,key){ 1085 const n = ('number'===typeof key) 1086 ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); 1087 if(0===n || !util.isInt32(n)){ 1088 toss3("Invalid bind() parameter name: "+key); 1089 } 1090 else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range."); 1091 return n; 1092 }; 1093 1094 /** 1095 If stmt._isLocked is truthy, this throws an exception 1096 complaining that the 2nd argument (an operation name, 1097 e.g. "bind()") is not legal while the statement is "locked". 1098 Locking happens before an exec()-like callback is passed a 1099 statement, to ensure that the callback does not mutate or 1100 finalize the statement. If it does not throw, it returns stmt. 1101 */ 1102 const affirmUnlocked = function(stmt,currentOpName){ 1103 if(stmt._isLocked){ 1104 toss3("Operation is illegal when statement is locked:",currentOpName); 1105 } 1106 return stmt; 1107 }; 1108 1109 /** 1110 Binds a single bound parameter value on the given stmt at the 1111 given index (numeric or named) using the given bindType (see 1112 the BindTypes enum) and value. Throws on error. Returns stmt on 1113 success. 1114 */ 1115 const bindOne = function f(stmt,ndx,bindType,val){ 1116 affirmUnlocked(stmt, 'bind()'); 1117 if(!f._){ 1118 if(capi.wasm.bigIntEnabled){ 1119 f._maxInt = BigInt("0x7fffffffffffffff"); 1120 f._minInt = ~f._maxInt; 1121 } 1122 /* Reminder: when not in BigInt mode, it's impossible for 1123 JS to represent a number out of the range we can bind, 1124 so we have no range checking. */ 1125 f._ = { 1126 string: function(stmt, ndx, val, asBlob){ 1127 if(1){ 1128 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1129 const stack = capi.wasm.scopedAllocPush(); 1130 try{ 1131 const n = capi.wasm.jstrlen(val); 1132 const pStr = capi.wasm.scopedAlloc(n); 1133 capi.wasm.jstrcpy(val, capi.wasm.heap8u(), pStr, n, false); 1134 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1135 return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); 1136 }finally{ 1137 capi.wasm.scopedAllocPop(stack); 1138 } 1139 }else{ 1140 const bytes = capi.wasm.jstrToUintArray(val,false); 1141 const pStr = capi.wasm.alloc(bytes.length || 1); 1142 capi.wasm.heap8u().set(bytes.length ? bytes : [0], pStr); 1143 try{ 1144 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1145 return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); 1146 }finally{ 1147 capi.wasm.dealloc(pStr); 1148 } 1149 } 1150 } 1151 }; 1152 } 1153 affirmSupportedBindType(val); 1154 ndx = affirmParamIndex(stmt,ndx); 1155 let rc = 0; 1156 switch((null===val || undefined===val) ? BindTypes.null : bindType){ 1157 case BindTypes.null: 1158 rc = capi.sqlite3_bind_null(stmt.pointer, ndx); 1159 break; 1160 case BindTypes.string: 1161 rc = f._.string(stmt, ndx, val, false); 1162 break; 1163 case BindTypes.number: { 1164 let m; 1165 if(util.isInt32(val)) m = capi.sqlite3_bind_int; 1166 else if(capi.wasm.bigIntEnabled && ('bigint'===typeof val)){ 1167 if(val<f._minInt || val>f._maxInt){ 1168 toss3("BigInt value is out of range for int64: "+val); 1169 } 1170 m = capi.sqlite3_bind_int64; 1171 }else if(Number.isInteger(val)){ 1172 m = capi.sqlite3_bind_int64; 1173 }else{ 1174 m = capi.sqlite3_bind_double; 1175 } 1176 rc = m(stmt.pointer, ndx, val); 1177 break; 1178 } 1179 case BindTypes.boolean: 1180 rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); 1181 break; 1182 case BindTypes.blob: { 1183 if('string'===typeof val){ 1184 rc = f._.string(stmt, ndx, val, true); 1185 }else if(!util.isBindableTypedArray(val)){ 1186 toss3("Binding a value as a blob requires", 1187 "that it be a string, Uint8Array, or Int8Array."); 1188 }else if(1){ 1189 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1190 const stack = capi.wasm.scopedAllocPush(); 1191 try{ 1192 const pBlob = capi.wasm.scopedAlloc(val.byteLength || 1); 1193 capi.wasm.heap8().set(val.byteLength ? val : [0], pBlob) 1194 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1195 capi.SQLITE_TRANSIENT); 1196 }finally{ 1197 capi.wasm.scopedAllocPop(stack); 1198 } 1199 }else{ 1200 const pBlob = capi.wasm.allocFromTypedArray(val); 1201 try{ 1202 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1203 capi.SQLITE_TRANSIENT); 1204 }finally{ 1205 capi.wasm.dealloc(pBlob); 1206 } 1207 } 1208 break; 1209 } 1210 default: 1211 console.warn("Unsupported bind() argument type:",val); 1212 toss3("Unsupported bind() argument type: "+(typeof val)); 1213 } 1214 if(rc) DB.checkRc(stmt.db.pointer, rc); 1215 return stmt; 1216 }; 1217 1218 Stmt.prototype = { 1219 /** 1220 "Finalizes" this statement. This is a no-op if the 1221 statement has already been finalizes. Returns 1222 undefined. Most methods in this class will throw if called 1223 after this is. 1224 */ 1225 finalize: function(){ 1226 if(this.pointer){ 1227 affirmUnlocked(this,'finalize()'); 1228 delete __stmtMap.get(this.db)[this.pointer]; 1229 capi.sqlite3_finalize(this.pointer); 1230 __ptrMap.delete(this); 1231 delete this._mayGet; 1232 delete this.columnCount; 1233 delete this.parameterCount; 1234 delete this.db; 1235 delete this._isLocked; 1236 } 1237 }, 1238 /** Clears all bound values. Returns this object. 1239 Throws if this statement has been finalized. */ 1240 clearBindings: function(){ 1241 affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') 1242 capi.sqlite3_clear_bindings(this.pointer); 1243 this._mayGet = false; 1244 return this; 1245 }, 1246 /** 1247 Resets this statement so that it may be step()ed again 1248 from the beginning. Returns this object. Throws if this 1249 statement has been finalized. 1250 1251 If passed a truthy argument then this.clearBindings() is 1252 also called, otherwise any existing bindings, along with 1253 any memory allocated for them, are retained. 1254 */ 1255 reset: function(alsoClearBinds){ 1256 affirmUnlocked(this,'reset()'); 1257 if(alsoClearBinds) this.clearBindings(); 1258 capi.sqlite3_reset(affirmStmtOpen(this).pointer); 1259 this._mayGet = false; 1260 return this; 1261 }, 1262 /** 1263 Binds one or more values to its bindable parameters. It 1264 accepts 1 or 2 arguments: 1265 1266 If passed a single argument, it must be either an array, an 1267 object, or a value of a bindable type (see below). 1268 1269 If passed 2 arguments, the first one is the 1-based bind 1270 index or bindable parameter name and the second one must be 1271 a value of a bindable type. 1272 1273 Bindable value types: 1274 1275 - null is bound as NULL. 1276 1277 - undefined as a standalone value is a no-op intended to 1278 simplify certain client-side use cases: passing undefined 1279 as a value to this function will not actually bind 1280 anything and this function will skip confirmation that 1281 binding is even legal. (Those semantics simplify certain 1282 client-side uses.) Conversely, a value of undefined as an 1283 array or object property when binding an array/object 1284 (see below) is treated the same as null. 1285 1286 - Numbers are bound as either doubles or integers: doubles 1287 if they are larger than 32 bits, else double or int32, 1288 depending on whether they have a fractional part. (It is, 1289 as of this writing, illegal to call (from JS) a WASM 1290 function which either takes or returns an int64.) 1291 Booleans are bound as integer 0 or 1. It is not expected 1292 the distinction of binding doubles which have no 1293 fractional parts is integers is significant for the 1294 majority of clients due to sqlite3's data typing 1295 model. If capi.wasm.bigIntEnabled is true then this 1296 routine will bind BigInt values as 64-bit integers. 1297 1298 - Strings are bound as strings (use bindAsBlob() to force 1299 blob binding). 1300 1301 - Uint8Array and Int8Array instances are bound as blobs. 1302 (TODO: binding the other TypedArray types.) 1303 1304 If passed an array, each element of the array is bound at 1305 the parameter index equal to the array index plus 1 1306 (because arrays are 0-based but binding is 1-based). 1307 1308 If passed an object, each object key is treated as a 1309 bindable parameter name. The object keys _must_ match any 1310 bindable parameter names, including any `$`, `@`, or `:` 1311 prefix. Because `$` is a legal identifier chararacter in 1312 JavaScript, that is the suggested prefix for bindable 1313 parameters: `stmt.bind({$a: 1, $b: 2})`. 1314 1315 It returns this object on success and throws on 1316 error. Errors include: 1317 1318 - Any bind index is out of range, a named bind parameter 1319 does not match, or this statement has no bindable 1320 parameters. 1321 1322 - Any value to bind is of an unsupported type. 1323 1324 - Passed no arguments or more than two. 1325 1326 - The statement has been finalized. 1327 */ 1328 bind: function(/*[ndx,] arg*/){ 1329 affirmStmtOpen(this); 1330 let ndx, arg; 1331 switch(arguments.length){ 1332 case 1: ndx = 1; arg = arguments[0]; break; 1333 case 2: ndx = arguments[0]; arg = arguments[1]; break; 1334 default: toss3("Invalid bind() arguments."); 1335 } 1336 if(undefined===arg){ 1337 /* It might seem intuitive to bind undefined as NULL 1338 but this approach simplifies certain client-side 1339 uses when passing on arguments between 2+ levels of 1340 functions. */ 1341 return this; 1342 }else if(!this.parameterCount){ 1343 toss3("This statement has no bindable parameters."); 1344 } 1345 this._mayGet = false; 1346 if(null===arg){ 1347 /* bind NULL */ 1348 return bindOne(this, ndx, BindTypes.null, arg); 1349 } 1350 else if(Array.isArray(arg)){ 1351 /* bind each entry by index */ 1352 if(1!==arguments.length){ 1353 toss3("When binding an array, an index argument is not permitted."); 1354 } 1355 arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); 1356 return this; 1357 } 1358 else if('object'===typeof arg/*null was checked above*/ 1359 && !util.isBindableTypedArray(arg)){ 1360 /* Treat each property of arg as a named bound parameter. */ 1361 if(1!==arguments.length){ 1362 toss3("When binding an object, an index argument is not permitted."); 1363 } 1364 Object.keys(arg) 1365 .forEach(k=>bindOne(this, k, 1366 affirmSupportedBindType(arg[k]), 1367 arg[k])); 1368 return this; 1369 }else{ 1370 return bindOne(this, ndx, affirmSupportedBindType(arg), arg); 1371 } 1372 toss3("Should not reach this point."); 1373 }, 1374 /** 1375 Special case of bind() which binds the given value using the 1376 BLOB binding mechanism instead of the default selected one for 1377 the value. The ndx may be a numbered or named bind index. The 1378 value must be of type string, null/undefined (both get treated 1379 as null), or a TypedArray of a type supported by the bind() 1380 API. 1381 1382 If passed a single argument, a bind index of 1 is assumed and 1383 the first argument is the value. 1384 */ 1385 bindAsBlob: function(ndx,arg){ 1386 affirmStmtOpen(this); 1387 if(1===arguments.length){ 1388 arg = ndx; 1389 ndx = 1; 1390 } 1391 const t = affirmSupportedBindType(arg); 1392 if(BindTypes.string !== t && BindTypes.blob !== t 1393 && BindTypes.null !== t){ 1394 toss3("Invalid value type for bindAsBlob()"); 1395 } 1396 bindOne(this, ndx, BindTypes.blob, arg); 1397 this._mayGet = false; 1398 return this; 1399 }, 1400 /** 1401 Steps the statement one time. If the result indicates that a 1402 row of data is available, a truthy value is returned. 1403 If no row of data is available, a falsy 1404 value is returned. Throws on error. 1405 */ 1406 step: function(){ 1407 affirmUnlocked(this, 'step()'); 1408 const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); 1409 switch(rc){ 1410 case capi.SQLITE_DONE: return this._mayGet = false; 1411 case capi.SQLITE_ROW: return this._mayGet = true; 1412 default: 1413 this._mayGet = false; 1414 console.warn("sqlite3_step() rc=",rc,"SQL =", 1415 capi.sqlite3_sql(this.pointer)); 1416 DB.checkRc(this.db.pointer, rc); 1417 } 1418 }, 1419 /** 1420 Functions exactly like step() except that... 1421 1422 1) On success, it calls this.reset() and returns this object. 1423 2) On error, it throws and does not call reset(). 1424 1425 This is intended to simplify constructs like: 1426 1427 ``` 1428 for(...) { 1429 stmt.bind(...).stepReset(); 1430 } 1431 ``` 1432 1433 Note that the reset() call makes it illegal to call this.get() 1434 after the step. 1435 */ 1436 stepReset: function(){ 1437 this.step(); 1438 return this.reset(); 1439 }, 1440 /** 1441 Functions like step() except that it finalizes this statement 1442 immediately after stepping unless the step cannot be performed 1443 because the statement is locked. Throws on error, but any error 1444 other than the statement-is-locked case will also trigger 1445 finalization of this statement. 1446 1447 On success, it returns true if the step indicated that a row of 1448 data was available, else it returns false. 1449 1450 This is intended to simplify use cases such as: 1451 1452 ``` 1453 aDb.prepare("insert in foo(a) values(?)").bind(123).stepFinalize(); 1454 ``` 1455 */ 1456 stepFinalize: function(){ 1457 const rc = this.step(); 1458 this.finalize(); 1459 return rc; 1460 }, 1461 /** 1462 Fetches the value from the given 0-based column index of 1463 the current data row, throwing if index is out of range. 1464 1465 Requires that step() has just returned a truthy value, else 1466 an exception is thrown. 1467 1468 By default it will determine the data type of the result 1469 automatically. If passed a second arugment, it must be one 1470 of the enumeration values for sqlite3 types, which are 1471 defined as members of the sqlite3 module: SQLITE_INTEGER, 1472 SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, 1473 except for undefined, will trigger an exception. Passing 1474 undefined is the same as not passing a value. It is legal 1475 to, e.g., fetch an integer value as a string, in which case 1476 sqlite3 will convert the value to a string. 1477 1478 If ndx is an array, this function behaves a differently: it 1479 assigns the indexes of the array, from 0 to the number of 1480 result columns, to the values of the corresponding column, 1481 and returns that array. 1482 1483 If ndx is a plain object, this function behaves even 1484 differentlier: it assigns the properties of the object to 1485 the values of their corresponding result columns. 1486 1487 Blobs are returned as Uint8Array instances. 1488 1489 Potential TODO: add type ID SQLITE_JSON, which fetches the 1490 result as a string and passes it (if it's not null) to 1491 JSON.parse(), returning the result of that. Until then, 1492 getJSON() can be used for that. 1493 */ 1494 get: function(ndx,asType){ 1495 if(!affirmStmtOpen(this)._mayGet){ 1496 toss3("Stmt.step() has not (recently) returned true."); 1497 } 1498 if(Array.isArray(ndx)){ 1499 let i = 0; 1500 while(i<this.columnCount){ 1501 ndx[i] = this.get(i++); 1502 } 1503 return ndx; 1504 }else if(ndx && 'object'===typeof ndx){ 1505 let i = 0; 1506 while(i<this.columnCount){ 1507 ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++); 1508 } 1509 return ndx; 1510 } 1511 affirmColIndex(this, ndx); 1512 switch(undefined===asType 1513 ? capi.sqlite3_column_type(this.pointer, ndx) 1514 : asType){ 1515 case capi.SQLITE_NULL: return null; 1516 case capi.SQLITE_INTEGER:{ 1517 if(capi.wasm.bigIntEnabled){ 1518 const rc = capi.sqlite3_column_int64(this.pointer, ndx); 1519 if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ 1520 /* Coerce "normal" number ranges to normal number values, 1521 and only return BigInt-type values for numbers out of this 1522 range. */ 1523 return Number(rc).valueOf(); 1524 } 1525 return rc; 1526 }else{ 1527 const rc = capi.sqlite3_column_double(this.pointer, ndx); 1528 if(rc>Number.MAX_SAFE_INTEGER || rc<Number.MIN_SAFE_INTEGER){ 1529 /* Throwing here is arguable but, since we're explicitly 1530 extracting an SQLITE_INTEGER-type value, it seems fair to throw 1531 if the extracted number is out of range for that type. 1532 This policy may be laxened to simply pass on the number and 1533 hope for the best, as the C API would do. */ 1534 toss3("Integer is out of range for JS integer range: "+rc); 1535 } 1536 //console.log("get integer rc=",rc,isInt32(rc)); 1537 return util.isInt32(rc) ? (rc | 0) : rc; 1538 } 1539 } 1540 case capi.SQLITE_FLOAT: 1541 return capi.sqlite3_column_double(this.pointer, ndx); 1542 case capi.SQLITE_TEXT: 1543 return capi.sqlite3_column_text(this.pointer, ndx); 1544 case capi.SQLITE_BLOB: { 1545 const n = capi.sqlite3_column_bytes(this.pointer, ndx), 1546 ptr = capi.sqlite3_column_blob(this.pointer, ndx), 1547 rc = new Uint8Array(n); 1548 //heap = n ? capi.wasm.heap8() : false; 1549 if(n) rc.set(capi.wasm.heap8u().slice(ptr, ptr+n), 0); 1550 //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; 1551 if(n && this.db._blobXfer instanceof Array){ 1552 /* This is an optimization soley for the 1553 Worker-based API. These values will be 1554 transfered to the main thread directly 1555 instead of being copied. */ 1556 this.db._blobXfer.push(rc.buffer); 1557 } 1558 return rc; 1559 } 1560 default: toss3("Don't know how to translate", 1561 "type of result column #"+ndx+"."); 1562 } 1563 toss3("Not reached."); 1564 }, 1565 /** Equivalent to get(ndx) but coerces the result to an 1566 integer. */ 1567 getInt: function(ndx){return this.get(ndx,capi.SQLITE_INTEGER)}, 1568 /** Equivalent to get(ndx) but coerces the result to a 1569 float. */ 1570 getFloat: function(ndx){return this.get(ndx,capi.SQLITE_FLOAT)}, 1571 /** Equivalent to get(ndx) but coerces the result to a 1572 string. */ 1573 getString: function(ndx){return this.get(ndx,capi.SQLITE_TEXT)}, 1574 /** Equivalent to get(ndx) but coerces the result to a 1575 Uint8Array. */ 1576 getBlob: function(ndx){return this.get(ndx,capi.SQLITE_BLOB)}, 1577 /** 1578 A convenience wrapper around get() which fetches the value 1579 as a string and then, if it is not null, passes it to 1580 JSON.parse(), returning that result. Throws if parsing 1581 fails. If the result is null, null is returned. An empty 1582 string, on the other hand, will trigger an exception. 1583 */ 1584 getJSON: function(ndx){ 1585 const s = this.get(ndx, capi.SQLITE_STRING); 1586 return null===s ? s : JSON.parse(s); 1587 }, 1588 // Design note: the only reason most of these getters have a 'get' 1589 // prefix is for consistency with getVALUE_TYPE(). The latter 1590 // arguablly really need that prefix for API readability and the 1591 // rest arguably don't, but consistency is a powerful thing. 1592 /** 1593 Returns the result column name of the given index, or 1594 throws if index is out of bounds or this statement has been 1595 finalized. This can be used without having run step() 1596 first. 1597 */ 1598 getColumnName: function(ndx){ 1599 return capi.sqlite3_column_name( 1600 affirmColIndex(affirmStmtOpen(this),ndx).pointer, ndx 1601 ); 1602 }, 1603 /** 1604 If this statement potentially has result columns, this 1605 function returns an array of all such names. If passed an 1606 array, it is used as the target and all names are appended 1607 to it. Returns the target array. Throws if this statement 1608 cannot have result columns. This object's columnCount member 1609 holds the number of columns. 1610 */ 1611 getColumnNames: function(tgt){ 1612 affirmColIndex(affirmStmtOpen(this),0); 1613 if(!tgt) tgt = []; 1614 for(let i = 0; i < this.columnCount; ++i){ 1615 tgt.push(capi.sqlite3_column_name(this.pointer, i)); 1616 } 1617 return tgt; 1618 }, 1619 /** 1620 If this statement has named bindable parameters and the 1621 given name matches one, its 1-based bind index is 1622 returned. If no match is found, 0 is returned. If it has no 1623 bindable parameters, the undefined value is returned. 1624 */ 1625 getParamIndex: function(name){ 1626 return (affirmStmtOpen(this).parameterCount 1627 ? capi.sqlite3_bind_parameter_index(this.pointer, name) 1628 : undefined); 1629 } 1630 }/*Stmt.prototype*/; 1631 1632 {/* Add the `pointer` property to DB and Stmt. */ 1633 const prop = { 1634 enumerable: true, 1635 get: function(){return __ptrMap.get(this)}, 1636 set: ()=>toss3("The pointer property is read-only.") 1637 } 1638 Object.defineProperty(Stmt.prototype, 'pointer', prop); 1639 Object.defineProperty(DB.prototype, 'pointer', prop); 1640 } 1641 1642 /** The OO API's public namespace. */ 1643 sqlite3.oo1 = { 1644 version: { 1645 lib: capi.sqlite3_libversion(), 1646 ooApi: "0.1" 1647 }, 1648 DB, 1649 Stmt, 1650 dbCtorHelper 1651 }/*oo1 object*/; 1652 1653}); 1654 1655