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