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