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