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