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 788 `callback.length` (i.e. the number of declared parameters it 789 has) **MINUS 1** (see below for why). As a special case, if 790 callback.length is 0, its arity is also 0 instead of -1. A 791 negative arity value means that the function is variadic and 792 may accept any number of arguments, up to sqlite3's 793 compile-time limits. sqlite3 will enforce the argument count if 794 is zero or greater. 795 796 The callback always receives a pointer to an `sqlite3_context` 797 object as its first argument. Any arguments after that are from 798 SQL code. The leading context argument does _not_ count towards 799 the function's arity. See the docs for 800 sqlite3.capi.sqlite3_create_function_v2() for why that argument 801 is needed in the interface. 802 803 The following properties correspond to flags documented at: 804 805 https://sqlite.org/c3ref/create_function.html 806 807 - .deterministic = SQLITE_DETERMINISTIC 808 - .directOnly = SQLITE_DIRECTONLY 809 - .innocuous = SQLITE_INNOCUOUS 810 */ 811 createFunction: function f(name, callback,opt){ 812 switch(arguments.length){ 813 case 1: /* (optionsObject) */ 814 opt = name; 815 name = opt.name; 816 callback = opt.callback; 817 break; 818 case 2: /* (name, callback|optionsObject) */ 819 if(!(callback instanceof Function)){ 820 opt = callback; 821 callback = opt.callback; 822 } 823 break; 824 default: break; 825 } 826 if(!opt) opt = {}; 827 if(!(callback instanceof Function)){ 828 toss3("Invalid arguments: expecting a callback function."); 829 }else if('string' !== typeof name){ 830 toss3("Invalid arguments: missing function name."); 831 } 832 let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; 833 if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; 834 if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; 835 if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS; 836 name = name.toLowerCase(); 837 DB.checkRc(this, capi.sqlite3_create_function_v2( 838 this.pointer, name, 839 (opt.hasOwnProperty('arity') 840 ? +opt.arity 841 : (callback.length ? callback.length-1/*for pCtx arg*/ : 0)), 842 capi.SQLITE_UTF8 | fFlags, null/*pApp*/, callback, 843 null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); 844 return this; 845 }/*createFunction()*/, 846 /** 847 Prepares the given SQL, step()s it one time, and returns 848 the value of the first result column. If it has no results, 849 undefined is returned. 850 851 If passed a second argument, it is treated like an argument 852 to Stmt.bind(), so may be any type supported by that 853 function. Passing the undefined value is the same as passing 854 no value, which is useful when... 855 856 If passed a 3rd argument, it is expected to be one of the 857 SQLITE_{typename} constants. Passing the undefined value is 858 the same as not passing a value. 859 860 Throws on error (e.g. malformed SQL). 861 */ 862 selectValue: function(sql,bind,asType){ 863 let stmt, rc; 864 try { 865 stmt = this.prepare(sql).bind(bind); 866 if(stmt.step()) rc = stmt.get(0,asType); 867 }finally{ 868 if(stmt) stmt.finalize(); 869 } 870 return rc; 871 }, 872 873 /** 874 Returns the number of currently-opened Stmt handles for this db 875 handle, or 0 if this DB instance is closed. 876 */ 877 openStatementCount: function(){ 878 return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; 879 }, 880 881 /** 882 Starts a transaction, calls the given callback, and then either 883 rolls back or commits the savepoint, depending on whether the 884 callback throws. The callback is passed this db object as its 885 only argument. On success, returns the result of the 886 callback. Throws on error. 887 888 Note that transactions may not be nested, so this will throw if 889 it is called recursively. For nested transactions, use the 890 savepoint() method or manually manage SAVEPOINTs using exec(). 891 */ 892 transaction: function(callback){ 893 affirmDbOpen(this).exec("BEGIN"); 894 try { 895 const rc = callback(this); 896 this.exec("COMMIT"); 897 return rc; 898 }catch(e){ 899 this.exec("ROLLBACK"); 900 throw e; 901 } 902 }, 903 904 /** 905 This works similarly to transaction() but uses sqlite3's SAVEPOINT 906 feature. This function starts a savepoint (with an unspecified name) 907 and calls the given callback function, passing it this db object. 908 If the callback returns, the savepoint is released (committed). If 909 the callback throws, the savepoint is rolled back. If it does not 910 throw, it returns the result of the callback. 911 */ 912 savepoint: function(callback){ 913 affirmDbOpen(this).exec("SAVEPOINT oo1"); 914 try { 915 const rc = callback(this); 916 this.exec("RELEASE oo1"); 917 return rc; 918 }catch(e){ 919 this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); 920 throw e; 921 } 922 } 923 }/*DB.prototype*/; 924 925 926 /** Throws if the given Stmt has been finalized, else stmt is 927 returned. */ 928 const affirmStmtOpen = function(stmt){ 929 if(!stmt.pointer) toss3("Stmt has been closed."); 930 return stmt; 931 }; 932 933 /** Returns an opaque truthy value from the BindTypes 934 enum if v's type is a valid bindable type, else 935 returns a falsy value. As a special case, a value of 936 undefined is treated as a bind type of null. */ 937 const isSupportedBindType = function(v){ 938 let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v]; 939 switch(t){ 940 case BindTypes.boolean: 941 case BindTypes.null: 942 case BindTypes.number: 943 case BindTypes.string: 944 return t; 945 case BindTypes.bigint: 946 if(wasm.bigIntEnabled) return t; 947 /* else fall through */ 948 default: 949 //console.log("isSupportedBindType",t,v); 950 return util.isBindableTypedArray(v) ? BindTypes.blob : undefined; 951 } 952 }; 953 954 /** 955 If isSupportedBindType(v) returns a truthy value, this 956 function returns that value, else it throws. 957 */ 958 const affirmSupportedBindType = function(v){ 959 //console.log('affirmSupportedBindType',v); 960 return isSupportedBindType(v) || toss3("Unsupported bind() argument type:",typeof v); 961 }; 962 963 /** 964 If key is a number and within range of stmt's bound parameter 965 count, key is returned. 966 967 If key is not a number then it is checked against named 968 parameters. If a match is found, its index is returned. 969 970 Else it throws. 971 */ 972 const affirmParamIndex = function(stmt,key){ 973 const n = ('number'===typeof key) 974 ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); 975 if(0===n || !util.isInt32(n)){ 976 toss3("Invalid bind() parameter name: "+key); 977 } 978 else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range."); 979 return n; 980 }; 981 982 /** 983 If stmt._isLocked is truthy, this throws an exception 984 complaining that the 2nd argument (an operation name, 985 e.g. "bind()") is not legal while the statement is "locked". 986 Locking happens before an exec()-like callback is passed a 987 statement, to ensure that the callback does not mutate or 988 finalize the statement. If it does not throw, it returns stmt. 989 */ 990 const affirmUnlocked = function(stmt,currentOpName){ 991 if(stmt._isLocked){ 992 toss3("Operation is illegal when statement is locked:",currentOpName); 993 } 994 return stmt; 995 }; 996 997 /** 998 Binds a single bound parameter value on the given stmt at the 999 given index (numeric or named) using the given bindType (see 1000 the BindTypes enum) and value. Throws on error. Returns stmt on 1001 success. 1002 */ 1003 const bindOne = function f(stmt,ndx,bindType,val){ 1004 affirmUnlocked(stmt, 'bind()'); 1005 if(!f._){ 1006 f._tooBigInt = (v)=>toss3( 1007 "BigInt value is too big to store without precision loss:", v 1008 ); 1009 /* Reminder: when not in BigInt mode, it's impossible for 1010 JS to represent a number out of the range we can bind, 1011 so we have no range checking. */ 1012 f._ = { 1013 string: function(stmt, ndx, val, asBlob){ 1014 if(1){ 1015 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1016 const stack = wasm.scopedAllocPush(); 1017 try{ 1018 const n = wasm.jstrlen(val); 1019 const pStr = wasm.scopedAlloc(n); 1020 wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false); 1021 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1022 return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); 1023 }finally{ 1024 wasm.scopedAllocPop(stack); 1025 } 1026 }else{ 1027 const bytes = wasm.jstrToUintArray(val,false); 1028 const pStr = wasm.alloc(bytes.length || 1); 1029 wasm.heap8u().set(bytes.length ? bytes : [0], pStr); 1030 try{ 1031 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 1032 return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); 1033 }finally{ 1034 wasm.dealloc(pStr); 1035 } 1036 } 1037 } 1038 }; 1039 }/* static init */ 1040 affirmSupportedBindType(val); 1041 ndx = affirmParamIndex(stmt,ndx); 1042 let rc = 0; 1043 switch((null===val || undefined===val) ? BindTypes.null : bindType){ 1044 case BindTypes.null: 1045 rc = capi.sqlite3_bind_null(stmt.pointer, ndx); 1046 break; 1047 case BindTypes.string: 1048 rc = f._.string(stmt, ndx, val, false); 1049 break; 1050 case BindTypes.number: { 1051 let m; 1052 if(util.isInt32(val)) m = capi.sqlite3_bind_int; 1053 else if('bigint'===typeof val){ 1054 if(!util.bigIntFits64(val)){ 1055 f._tooBigInt(val); 1056 }else if(wasm.bigIntEnabled){ 1057 m = capi.sqlite3_bind_int64; 1058 }else if(util.bigIntFitsDouble(val)){ 1059 val = Number(val); 1060 m = capi.sqlite3_bind_double; 1061 }else{ 1062 f._tooBigInt(val); 1063 } 1064 }else{ // !int32, !bigint 1065 val = Number(val); 1066 if(wasm.bigIntEnabled && Number.isInteger(val)){ 1067 m = capi.sqlite3_bind_int64; 1068 }else{ 1069 m = capi.sqlite3_bind_double; 1070 } 1071 } 1072 rc = m(stmt.pointer, ndx, val); 1073 break; 1074 } 1075 case BindTypes.boolean: 1076 rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); 1077 break; 1078 case BindTypes.blob: { 1079 if('string'===typeof val){ 1080 rc = f._.string(stmt, ndx, val, true); 1081 }else if(!util.isBindableTypedArray(val)){ 1082 toss3("Binding a value as a blob requires", 1083 "that it be a string, Uint8Array, or Int8Array."); 1084 }else if(1){ 1085 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1086 const stack = wasm.scopedAllocPush(); 1087 try{ 1088 const pBlob = wasm.scopedAlloc(val.byteLength || 1); 1089 wasm.heap8().set(val.byteLength ? val : [0], pBlob) 1090 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1091 capi.SQLITE_TRANSIENT); 1092 }finally{ 1093 wasm.scopedAllocPop(stack); 1094 } 1095 }else{ 1096 const pBlob = wasm.allocFromTypedArray(val); 1097 try{ 1098 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1099 capi.SQLITE_TRANSIENT); 1100 }finally{ 1101 wasm.dealloc(pBlob); 1102 } 1103 } 1104 break; 1105 } 1106 default: 1107 console.warn("Unsupported bind() argument type:",val); 1108 toss3("Unsupported bind() argument type: "+(typeof val)); 1109 } 1110 if(rc) DB.checkRc(stmt.db.pointer, rc); 1111 return stmt; 1112 }; 1113 1114 Stmt.prototype = { 1115 /** 1116 "Finalizes" this statement. This is a no-op if the 1117 statement has already been finalizes. Returns 1118 undefined. Most methods in this class will throw if called 1119 after this is. 1120 */ 1121 finalize: function(){ 1122 if(this.pointer){ 1123 affirmUnlocked(this,'finalize()'); 1124 delete __stmtMap.get(this.db)[this.pointer]; 1125 capi.sqlite3_finalize(this.pointer); 1126 __ptrMap.delete(this); 1127 delete this._mayGet; 1128 delete this.columnCount; 1129 delete this.parameterCount; 1130 delete this.db; 1131 delete this._isLocked; 1132 } 1133 }, 1134 /** Clears all bound values. Returns this object. 1135 Throws if this statement has been finalized. */ 1136 clearBindings: function(){ 1137 affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') 1138 capi.sqlite3_clear_bindings(this.pointer); 1139 this._mayGet = false; 1140 return this; 1141 }, 1142 /** 1143 Resets this statement so that it may be step()ed again 1144 from the beginning. Returns this object. Throws if this 1145 statement has been finalized. 1146 1147 If passed a truthy argument then this.clearBindings() is 1148 also called, otherwise any existing bindings, along with 1149 any memory allocated for them, are retained. 1150 */ 1151 reset: function(alsoClearBinds){ 1152 affirmUnlocked(this,'reset()'); 1153 if(alsoClearBinds) this.clearBindings(); 1154 capi.sqlite3_reset(affirmStmtOpen(this).pointer); 1155 this._mayGet = false; 1156 return this; 1157 }, 1158 /** 1159 Binds one or more values to its bindable parameters. It 1160 accepts 1 or 2 arguments: 1161 1162 If passed a single argument, it must be either an array, an 1163 object, or a value of a bindable type (see below). 1164 1165 If passed 2 arguments, the first one is the 1-based bind 1166 index or bindable parameter name and the second one must be 1167 a value of a bindable type. 1168 1169 Bindable value types: 1170 1171 - null is bound as NULL. 1172 1173 - undefined as a standalone value is a no-op intended to 1174 simplify certain client-side use cases: passing undefined as 1175 a value to this function will not actually bind anything and 1176 this function will skip confirmation that binding is even 1177 legal. (Those semantics simplify certain client-side uses.) 1178 Conversely, a value of undefined as an array or object 1179 property when binding an array/object (see below) is treated 1180 the same as null. 1181 1182 - Numbers are bound as either doubles or integers: doubles if 1183 they are larger than 32 bits, else double or int32, depending 1184 on whether they have a fractional part. Booleans are bound as 1185 integer 0 or 1. It is not expected the distinction of binding 1186 doubles which have no fractional parts is integers is 1187 significant for the majority of clients due to sqlite3's data 1188 typing model. If [BigInt] support is enabled then this 1189 routine will bind BigInt values as 64-bit integers if they'll 1190 fit in 64 bits. If that support disabled, it will store the 1191 BigInt as an int32 or a double if it can do so without loss 1192 of precision. If the BigInt is _too BigInt_ then it will 1193 throw. 1194 1195 - Strings are bound as strings (use bindAsBlob() to force 1196 blob binding). 1197 1198 - Uint8Array and Int8Array instances are bound as blobs. 1199 (TODO: binding the other TypedArray types.) 1200 1201 If passed an array, each element of the array is bound at 1202 the parameter index equal to the array index plus 1 1203 (because arrays are 0-based but binding is 1-based). 1204 1205 If passed an object, each object key is treated as a 1206 bindable parameter name. The object keys _must_ match any 1207 bindable parameter names, including any `$`, `@`, or `:` 1208 prefix. Because `$` is a legal identifier chararacter in 1209 JavaScript, that is the suggested prefix for bindable 1210 parameters: `stmt.bind({$a: 1, $b: 2})`. 1211 1212 It returns this object on success and throws on 1213 error. Errors include: 1214 1215 - Any bind index is out of range, a named bind parameter 1216 does not match, or this statement has no bindable 1217 parameters. 1218 1219 - Any value to bind is of an unsupported type. 1220 1221 - Passed no arguments or more than two. 1222 1223 - The statement has been finalized. 1224 */ 1225 bind: function(/*[ndx,] arg*/){ 1226 affirmStmtOpen(this); 1227 let ndx, arg; 1228 switch(arguments.length){ 1229 case 1: ndx = 1; arg = arguments[0]; break; 1230 case 2: ndx = arguments[0]; arg = arguments[1]; break; 1231 default: toss3("Invalid bind() arguments."); 1232 } 1233 if(undefined===arg){ 1234 /* It might seem intuitive to bind undefined as NULL 1235 but this approach simplifies certain client-side 1236 uses when passing on arguments between 2+ levels of 1237 functions. */ 1238 return this; 1239 }else if(!this.parameterCount){ 1240 toss3("This statement has no bindable parameters."); 1241 } 1242 this._mayGet = false; 1243 if(null===arg){ 1244 /* bind NULL */ 1245 return bindOne(this, ndx, BindTypes.null, arg); 1246 } 1247 else if(Array.isArray(arg)){ 1248 /* bind each entry by index */ 1249 if(1!==arguments.length){ 1250 toss3("When binding an array, an index argument is not permitted."); 1251 } 1252 arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); 1253 return this; 1254 } 1255 else if('object'===typeof arg/*null was checked above*/ 1256 && !util.isBindableTypedArray(arg)){ 1257 /* Treat each property of arg as a named bound parameter. */ 1258 if(1!==arguments.length){ 1259 toss3("When binding an object, an index argument is not permitted."); 1260 } 1261 Object.keys(arg) 1262 .forEach(k=>bindOne(this, k, 1263 affirmSupportedBindType(arg[k]), 1264 arg[k])); 1265 return this; 1266 }else{ 1267 return bindOne(this, ndx, affirmSupportedBindType(arg), arg); 1268 } 1269 toss3("Should not reach this point."); 1270 }, 1271 /** 1272 Special case of bind() which binds the given value using the 1273 BLOB binding mechanism instead of the default selected one for 1274 the value. The ndx may be a numbered or named bind index. The 1275 value must be of type string, null/undefined (both get treated 1276 as null), or a TypedArray of a type supported by the bind() 1277 API. 1278 1279 If passed a single argument, a bind index of 1 is assumed and 1280 the first argument is the value. 1281 */ 1282 bindAsBlob: function(ndx,arg){ 1283 affirmStmtOpen(this); 1284 if(1===arguments.length){ 1285 arg = ndx; 1286 ndx = 1; 1287 } 1288 const t = affirmSupportedBindType(arg); 1289 if(BindTypes.string !== t && BindTypes.blob !== t 1290 && BindTypes.null !== t){ 1291 toss3("Invalid value type for bindAsBlob()"); 1292 } 1293 bindOne(this, ndx, BindTypes.blob, arg); 1294 this._mayGet = false; 1295 return this; 1296 }, 1297 /** 1298 Steps the statement one time. If the result indicates that a 1299 row of data is available, a truthy value is returned. 1300 If no row of data is available, a falsy 1301 value is returned. Throws on error. 1302 */ 1303 step: function(){ 1304 affirmUnlocked(this, 'step()'); 1305 const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); 1306 switch(rc){ 1307 case capi.SQLITE_DONE: return this._mayGet = false; 1308 case capi.SQLITE_ROW: return this._mayGet = true; 1309 default: 1310 this._mayGet = false; 1311 console.warn("sqlite3_step() rc=",rc, 1312 capi.sqlite3_web_rc_str(rc), 1313 "SQL =", capi.sqlite3_sql(this.pointer)); 1314 DB.checkRc(this.db.pointer, rc); 1315 } 1316 }, 1317 /** 1318 Functions exactly like step() except that... 1319 1320 1) On success, it calls this.reset() and returns this object. 1321 2) On error, it throws and does not call reset(). 1322 1323 This is intended to simplify constructs like: 1324 1325 ``` 1326 for(...) { 1327 stmt.bind(...).stepReset(); 1328 } 1329 ``` 1330 1331 Note that the reset() call makes it illegal to call this.get() 1332 after the step. 1333 */ 1334 stepReset: function(){ 1335 this.step(); 1336 return this.reset(); 1337 }, 1338 /** 1339 Functions like step() except that it finalizes this statement 1340 immediately after stepping unless the step cannot be performed 1341 because the statement is locked. Throws on error, but any error 1342 other than the statement-is-locked case will also trigger 1343 finalization of this statement. 1344 1345 On success, it returns true if the step indicated that a row of 1346 data was available, else it returns false. 1347 1348 This is intended to simplify use cases such as: 1349 1350 ``` 1351 aDb.prepare("insert into foo(a) values(?)").bind(123).stepFinalize(); 1352 ``` 1353 */ 1354 stepFinalize: function(){ 1355 const rc = this.step(); 1356 this.finalize(); 1357 return rc; 1358 }, 1359 /** 1360 Fetches the value from the given 0-based column index of 1361 the current data row, throwing if index is out of range. 1362 1363 Requires that step() has just returned a truthy value, else 1364 an exception is thrown. 1365 1366 By default it will determine the data type of the result 1367 automatically. If passed a second arugment, it must be one 1368 of the enumeration values for sqlite3 types, which are 1369 defined as members of the sqlite3 module: SQLITE_INTEGER, 1370 SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, 1371 except for undefined, will trigger an exception. Passing 1372 undefined is the same as not passing a value. It is legal 1373 to, e.g., fetch an integer value as a string, in which case 1374 sqlite3 will convert the value to a string. 1375 1376 If ndx is an array, this function behaves a differently: it 1377 assigns the indexes of the array, from 0 to the number of 1378 result columns, to the values of the corresponding column, 1379 and returns that array. 1380 1381 If ndx is a plain object, this function behaves even 1382 differentlier: it assigns the properties of the object to 1383 the values of their corresponding result columns. 1384 1385 Blobs are returned as Uint8Array instances. 1386 1387 Potential TODO: add type ID SQLITE_JSON, which fetches the 1388 result as a string and passes it (if it's not null) to 1389 JSON.parse(), returning the result of that. Until then, 1390 getJSON() can be used for that. 1391 */ 1392 get: function(ndx,asType){ 1393 if(!affirmStmtOpen(this)._mayGet){ 1394 toss3("Stmt.step() has not (recently) returned true."); 1395 } 1396 if(Array.isArray(ndx)){ 1397 let i = 0; 1398 while(i<this.columnCount){ 1399 ndx[i] = this.get(i++); 1400 } 1401 return ndx; 1402 }else if(ndx && 'object'===typeof ndx){ 1403 let i = 0; 1404 while(i<this.columnCount){ 1405 ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++); 1406 } 1407 return ndx; 1408 } 1409 affirmColIndex(this, ndx); 1410 switch(undefined===asType 1411 ? capi.sqlite3_column_type(this.pointer, ndx) 1412 : asType){ 1413 case capi.SQLITE_NULL: return null; 1414 case capi.SQLITE_INTEGER:{ 1415 if(wasm.bigIntEnabled){ 1416 const rc = capi.sqlite3_column_int64(this.pointer, ndx); 1417 if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ 1418 /* Coerce "normal" number ranges to normal number values, 1419 and only return BigInt-type values for numbers out of this 1420 range. */ 1421 return Number(rc).valueOf(); 1422 } 1423 return rc; 1424 }else{ 1425 const rc = capi.sqlite3_column_double(this.pointer, ndx); 1426 if(rc>Number.MAX_SAFE_INTEGER || rc<Number.MIN_SAFE_INTEGER){ 1427 /* Throwing here is arguable but, since we're explicitly 1428 extracting an SQLITE_INTEGER-type value, it seems fair to throw 1429 if the extracted number is out of range for that type. 1430 This policy may be laxened to simply pass on the number and 1431 hope for the best, as the C API would do. */ 1432 toss3("Integer is out of range for JS integer range: "+rc); 1433 } 1434 //console.log("get integer rc=",rc,isInt32(rc)); 1435 return util.isInt32(rc) ? (rc | 0) : rc; 1436 } 1437 } 1438 case capi.SQLITE_FLOAT: 1439 return capi.sqlite3_column_double(this.pointer, ndx); 1440 case capi.SQLITE_TEXT: 1441 return capi.sqlite3_column_text(this.pointer, ndx); 1442 case capi.SQLITE_BLOB: { 1443 const n = capi.sqlite3_column_bytes(this.pointer, ndx), 1444 ptr = capi.sqlite3_column_blob(this.pointer, ndx), 1445 rc = new Uint8Array(n); 1446 //heap = n ? wasm.heap8() : false; 1447 if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0); 1448 //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; 1449 if(n && this.db._blobXfer instanceof Array){ 1450 /* This is an optimization soley for the 1451 Worker-based API. These values will be 1452 transfered to the main thread directly 1453 instead of being copied. */ 1454 this.db._blobXfer.push(rc.buffer); 1455 } 1456 return rc; 1457 } 1458 default: toss3("Don't know how to translate", 1459 "type of result column #"+ndx+"."); 1460 } 1461 toss3("Not reached."); 1462 }, 1463 /** Equivalent to get(ndx) but coerces the result to an 1464 integer. */ 1465 getInt: function(ndx){return this.get(ndx,capi.SQLITE_INTEGER)}, 1466 /** Equivalent to get(ndx) but coerces the result to a 1467 float. */ 1468 getFloat: function(ndx){return this.get(ndx,capi.SQLITE_FLOAT)}, 1469 /** Equivalent to get(ndx) but coerces the result to a 1470 string. */ 1471 getString: function(ndx){return this.get(ndx,capi.SQLITE_TEXT)}, 1472 /** Equivalent to get(ndx) but coerces the result to a 1473 Uint8Array. */ 1474 getBlob: function(ndx){return this.get(ndx,capi.SQLITE_BLOB)}, 1475 /** 1476 A convenience wrapper around get() which fetches the value 1477 as a string and then, if it is not null, passes it to 1478 JSON.parse(), returning that result. Throws if parsing 1479 fails. If the result is null, null is returned. An empty 1480 string, on the other hand, will trigger an exception. 1481 */ 1482 getJSON: function(ndx){ 1483 const s = this.get(ndx, capi.SQLITE_STRING); 1484 return null===s ? s : JSON.parse(s); 1485 }, 1486 // Design note: the only reason most of these getters have a 'get' 1487 // prefix is for consistency with getVALUE_TYPE(). The latter 1488 // arguably really need that prefix for API readability and the 1489 // rest arguably don't, but consistency is a powerful thing. 1490 /** 1491 Returns the result column name of the given index, or 1492 throws if index is out of bounds or this statement has been 1493 finalized. This can be used without having run step() 1494 first. 1495 */ 1496 getColumnName: function(ndx){ 1497 return capi.sqlite3_column_name( 1498 affirmColIndex(affirmStmtOpen(this),ndx).pointer, ndx 1499 ); 1500 }, 1501 /** 1502 If this statement potentially has result columns, this 1503 function returns an array of all such names. If passed an 1504 array, it is used as the target and all names are appended 1505 to it. Returns the target array. Throws if this statement 1506 cannot have result columns. This object's columnCount member 1507 holds the number of columns. 1508 */ 1509 getColumnNames: function(tgt=[]){ 1510 affirmColIndex(affirmStmtOpen(this),0); 1511 for(let i = 0; i < this.columnCount; ++i){ 1512 tgt.push(capi.sqlite3_column_name(this.pointer, i)); 1513 } 1514 return tgt; 1515 }, 1516 /** 1517 If this statement has named bindable parameters and the 1518 given name matches one, its 1-based bind index is 1519 returned. If no match is found, 0 is returned. If it has no 1520 bindable parameters, the undefined value is returned. 1521 */ 1522 getParamIndex: function(name){ 1523 return (affirmStmtOpen(this).parameterCount 1524 ? capi.sqlite3_bind_parameter_index(this.pointer, name) 1525 : undefined); 1526 } 1527 }/*Stmt.prototype*/; 1528 1529 {/* Add the `pointer` property to DB and Stmt. */ 1530 const prop = { 1531 enumerable: true, 1532 get: function(){return __ptrMap.get(this)}, 1533 set: ()=>toss3("The pointer property is read-only.") 1534 } 1535 Object.defineProperty(Stmt.prototype, 'pointer', prop); 1536 Object.defineProperty(DB.prototype, 'pointer', prop); 1537 } 1538 1539 /** The OO API's public namespace. */ 1540 sqlite3.oo1 = { 1541 version: { 1542 lib: capi.sqlite3_libversion(), 1543 ooApi: "0.1" 1544 }, 1545 DB, 1546 Stmt, 1547 dbCtorHelper 1548 }/*oo1 object*/; 1549 1550 if(util.isMainWindow()){ 1551 /** 1552 Functionally equivalent to DB(storageName,'c','kvvfs') except 1553 that it throws if the given storage name is not one of 'local' 1554 or 'session'. 1555 */ 1556 sqlite3.oo1.JsStorageDb = function(storageName='session'){ 1557 if('session'!==storageName && 'local'!==storageName){ 1558 toss3("JsStorageDb db name must be one of 'session' or 'local'."); 1559 } 1560 dbCtorHelper.call(this, { 1561 filename: storageName, 1562 flags: 'c', 1563 vfs: "kvvfs" 1564 }); 1565 }; 1566 const jdb = sqlite3.oo1.JsStorageDb; 1567 jdb.prototype = Object.create(DB.prototype); 1568 /** Equivalent to sqlite3_web_kvvfs_clear(). */ 1569 jdb.clearStorage = capi.sqlite3_web_kvvfs_clear; 1570 /** 1571 Clears this database instance's storage or throws if this 1572 instance has been closed. Returns the number of 1573 database blocks which were cleaned up. 1574 */ 1575 jdb.prototype.clearStorage = function(){ 1576 return jdb.clearStorage(affirmDbOpen(this).filename); 1577 }; 1578 /** Equivalent to sqlite3_web_kvvfs_size(). */ 1579 jdb.storageSize = capi.sqlite3_web_kvvfs_size; 1580 /** 1581 Returns the _approximate_ number of bytes this database takes 1582 up in its storage or throws if this instance has been closed. 1583 */ 1584 jdb.prototype.storageSize = function(){ 1585 return jdb.storageSize(affirmDbOpen(this).filename); 1586 }; 1587 }/*main-window-only bits*/ 1588 1589}); 1590 1591