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