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(resultRows) resultRows.push(row); 579 if(callback) callback(row, stmt); 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 Starts a transaction, calls the given callback, and then either 824 rolls back or commits the transaction, depending on whether the 825 callback throw. The callback is pass this db object as its only 826 argument. On success, returns the result of the callback. 827 Throws on error. 828 */ 829 callInTransaction: function(callback){ 830 affirmDbOpen(this); 831 let err, rc; 832 this.exec("BEGIN"); 833 try { rc = callback(this); } 834 catch(e){ 835 err = e; 836 throw e; 837 }finally{ 838 if(err) this.exec("ROLLBACK"); 839 else this.exec("COMMIT"); 840 } 841 return rc; 842 }, 843 844 /** 845 This function currently does nothing and always throws. It 846 WILL BE REMOVED pending other refactoring, to eliminate a hard 847 dependency on Emscripten. This feature will be moved into a 848 higher-level API or a runtime-configurable feature. 849 850 That said, what its replacement should eventually do is... 851 852 Exports a copy of this db's file as a Uint8Array and 853 returns it. It is technically not legal to call this while 854 any prepared statement are currently active because, 855 depending on the platform, it might not be legal to read 856 the db while a statement is locking it. Throws if this db 857 is not open or has any opened statements. 858 859 The resulting buffer can be passed to this class's 860 constructor to restore the DB. 861 862 Maintenance reminder: the corresponding sql.js impl of this 863 feature closes the current db, finalizing any active 864 statements and (seemingly unnecessarily) destroys any UDFs, 865 copies the file, and then re-opens it (without restoring 866 the UDFs). Those gymnastics are not necessary on the tested 867 platform but might be necessary on others. Because of that 868 eventuality, this interface currently enforces that no 869 statements are active when this is run. It will throw if 870 any are. 871 */ 872 exportBinaryImage: function(){ 873 toss3("exportBinaryImage() is slated for removal for portability reasons."); 874 /*********************** 875 The following is currently kept only for reference when 876 porting to some other layer, noting that we may well not be 877 able to implement this, at this level, when using the OPFS 878 VFS because of its exclusive locking policy. 879 880 affirmDbOpen(this); 881 if(this.openStatementCount()>0){ 882 toss3("Cannot export with prepared statements active!", 883 "finalize() all statements and try again."); 884 } 885 return MODCFG.FS.readFile(this.filename, {encoding:"binary"}); 886 ***********************/ 887 } 888 }/*DB.prototype*/; 889 890 891 /** Throws if the given Stmt has been finalized, else stmt is 892 returned. */ 893 const affirmStmtOpen = function(stmt){ 894 if(!stmt.pointer) toss3("Stmt has been closed."); 895 return stmt; 896 }; 897 898 /** Returns an opaque truthy value from the BindTypes 899 enum if v's type is a valid bindable type, else 900 returns a falsy value. As a special case, a value of 901 undefined is treated as a bind type of null. */ 902 const isSupportedBindType = function(v){ 903 let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v]; 904 switch(t){ 905 case BindTypes.boolean: 906 case BindTypes.null: 907 case BindTypes.number: 908 case BindTypes.string: 909 return t; 910 case BindTypes.bigint: 911 if(capi.wasm.bigIntEnabled) return t; 912 /* else fall through */ 913 default: 914 //console.log("isSupportedBindType",t,v); 915 return util.isBindableTypedArray(v) ? BindTypes.blob : undefined; 916 } 917 }; 918 919 /** 920 If isSupportedBindType(v) returns a truthy value, this 921 function returns that value, else it throws. 922 */ 923 const affirmSupportedBindType = function(v){ 924 //console.log('affirmSupportedBindType',v); 925 return isSupportedBindType(v) || toss3("Unsupported bind() argument type:",typeof v); 926 }; 927 928 /** 929 If key is a number and within range of stmt's bound parameter 930 count, key is returned. 931 932 If key is not a number then it is checked against named 933 parameters. If a match is found, its index is returned. 934 935 Else it throws. 936 */ 937 const affirmParamIndex = function(stmt,key){ 938 const n = ('number'===typeof key) 939 ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); 940 if(0===n || !util.isInt32(n)){ 941 toss3("Invalid bind() parameter name: "+key); 942 } 943 else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range."); 944 return n; 945 }; 946 947 /** 948 If stmt._isLocked is truthy, this throws an exception 949 complaining that the 2nd argument (an operation name, 950 e.g. "bind()") is not legal while the statement is "locked". 951 Locking happens before an exec()-like callback is passed a 952 statement, to ensure that the callback does not mutate or 953 finalize the statement. If it does not throw, it returns stmt. 954 */ 955 const affirmUnlocked = function(stmt,currentOpName){ 956 if(stmt._isLocked){ 957 toss3("Operation is illegal when statement is locked:",currentOpName); 958 } 959 return stmt; 960 }; 961 962 /** 963 Binds a single bound parameter value on the given stmt at the 964 given index (numeric or named) using the given bindType (see 965 the BindTypes enum) and value. Throws on error. Returns stmt on 966 success. 967 */ 968 const bindOne = function f(stmt,ndx,bindType,val){ 969 affirmUnlocked(stmt, 'bind()'); 970 if(!f._){ 971 if(capi.wasm.bigIntEnabled){ 972 f._maxInt = BigInt("0x7fffffffffffffff"); 973 f._minInt = ~f._maxInt; 974 } 975 /* Reminder: when not in BigInt mode, it's impossible for 976 JS to represent a number out of the range we can bind, 977 so we have no range checking. */ 978 f._ = { 979 string: function(stmt, ndx, val, asBlob){ 980 if(1){ 981 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 982 const stack = capi.wasm.scopedAllocPush(); 983 try{ 984 const n = capi.wasm.jstrlen(val); 985 const pStr = capi.wasm.scopedAlloc(n); 986 capi.wasm.jstrcpy(val, capi.wasm.heap8u(), pStr, n, false); 987 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 988 return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); 989 }finally{ 990 capi.wasm.scopedAllocPop(stack); 991 } 992 }else{ 993 const bytes = capi.wasm.jstrToUintArray(val,false); 994 const pStr = capi.wasm.alloc(bytes.length || 1); 995 capi.wasm.heap8u().set(bytes.length ? bytes : [0], pStr); 996 try{ 997 const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; 998 return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); 999 }finally{ 1000 capi.wasm.dealloc(pStr); 1001 } 1002 } 1003 } 1004 }; 1005 } 1006 affirmSupportedBindType(val); 1007 ndx = affirmParamIndex(stmt,ndx); 1008 let rc = 0; 1009 switch((null===val || undefined===val) ? BindTypes.null : bindType){ 1010 case BindTypes.null: 1011 rc = capi.sqlite3_bind_null(stmt.pointer, ndx); 1012 break; 1013 case BindTypes.string: 1014 rc = f._.string(stmt, ndx, val, false); 1015 break; 1016 case BindTypes.number: { 1017 let m; 1018 if(util.isInt32(val)) m = capi.sqlite3_bind_int; 1019 else if(capi.wasm.bigIntEnabled && ('bigint'===typeof val)){ 1020 if(val<f._minInt || val>f._maxInt){ 1021 toss3("BigInt value is out of range for int64: "+val); 1022 } 1023 m = capi.sqlite3_bind_int64; 1024 }else if(Number.isInteger(val)){ 1025 m = capi.sqlite3_bind_int64; 1026 }else{ 1027 m = capi.sqlite3_bind_double; 1028 } 1029 rc = m(stmt.pointer, ndx, val); 1030 break; 1031 } 1032 case BindTypes.boolean: 1033 rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); 1034 break; 1035 case BindTypes.blob: { 1036 if('string'===typeof val){ 1037 rc = f._.string(stmt, ndx, val, true); 1038 }else if(!util.isBindableTypedArray(val)){ 1039 toss3("Binding a value as a blob requires", 1040 "that it be a string, Uint8Array, or Int8Array."); 1041 }else if(1){ 1042 /* _Hypothetically_ more efficient than the impl in the 'else' block. */ 1043 const stack = capi.wasm.scopedAllocPush(); 1044 try{ 1045 const pBlob = capi.wasm.scopedAlloc(val.byteLength || 1); 1046 capi.wasm.heap8().set(val.byteLength ? val : [0], pBlob) 1047 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1048 capi.SQLITE_TRANSIENT); 1049 }finally{ 1050 capi.wasm.scopedAllocPop(stack); 1051 } 1052 }else{ 1053 const pBlob = capi.wasm.mallocFromTypedArray(val); 1054 try{ 1055 rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, 1056 capi.SQLITE_TRANSIENT); 1057 }finally{ 1058 capi.wasm.dealloc(pBlob); 1059 } 1060 } 1061 break; 1062 } 1063 default: 1064 console.warn("Unsupported bind() argument type:",val); 1065 toss3("Unsupported bind() argument type: "+(typeof val)); 1066 } 1067 if(rc) DB.checkRc(stmt.db.pointer, rc); 1068 return stmt; 1069 }; 1070 1071 Stmt.prototype = { 1072 /** 1073 "Finalizes" this statement. This is a no-op if the 1074 statement has already been finalizes. Returns 1075 undefined. Most methods in this class will throw if called 1076 after this is. 1077 */ 1078 finalize: function(){ 1079 if(this.pointer){ 1080 affirmUnlocked(this,'finalize()'); 1081 delete __stmtMap.get(this.db)[this.pointer]; 1082 capi.sqlite3_finalize(this.pointer); 1083 __ptrMap.delete(this); 1084 delete this.columnCount; 1085 delete this.parameterCount; 1086 delete this.db; 1087 delete this._isLocked; 1088 } 1089 }, 1090 /** Clears all bound values. Returns this object. 1091 Throws if this statement has been finalized. */ 1092 clearBindings: function(){ 1093 affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') 1094 capi.sqlite3_clear_bindings(this.pointer); 1095 this._mayGet = false; 1096 return this; 1097 }, 1098 /** 1099 Resets this statement so that it may be step()ed again 1100 from the beginning. Returns this object. Throws if this 1101 statement has been finalized. 1102 1103 If passed a truthy argument then this.clearBindings() is 1104 also called, otherwise any existing bindings, along with 1105 any memory allocated for them, are retained. 1106 */ 1107 reset: function(alsoClearBinds){ 1108 affirmUnlocked(this,'reset()'); 1109 if(alsoClearBinds) this.clearBindings(); 1110 capi.sqlite3_reset(affirmStmtOpen(this).pointer); 1111 this._mayGet = false; 1112 return this; 1113 }, 1114 /** 1115 Binds one or more values to its bindable parameters. It 1116 accepts 1 or 2 arguments: 1117 1118 If passed a single argument, it must be either an array, an 1119 object, or a value of a bindable type (see below). 1120 1121 If passed 2 arguments, the first one is the 1-based bind 1122 index or bindable parameter name and the second one must be 1123 a value of a bindable type. 1124 1125 Bindable value types: 1126 1127 - null is bound as NULL. 1128 1129 - undefined as a standalone value is a no-op intended to 1130 simplify certain client-side use cases: passing undefined 1131 as a value to this function will not actually bind 1132 anything and this function will skip confirmation that 1133 binding is even legal. (Those semantics simplify certain 1134 client-side uses.) Conversely, a value of undefined as an 1135 array or object property when binding an array/object 1136 (see below) is treated the same as null. 1137 1138 - Numbers are bound as either doubles or integers: doubles 1139 if they are larger than 32 bits, else double or int32, 1140 depending on whether they have a fractional part. (It is, 1141 as of this writing, illegal to call (from JS) a WASM 1142 function which either takes or returns an int64.) 1143 Booleans are bound as integer 0 or 1. It is not expected 1144 the distinction of binding doubles which have no 1145 fractional parts is integers is significant for the 1146 majority of clients due to sqlite3's data typing 1147 model. If capi.wasm.bigIntEnabled is true then this 1148 routine will bind BigInt values as 64-bit integers. 1149 1150 - Strings are bound as strings (use bindAsBlob() to force 1151 blob binding). 1152 1153 - Uint8Array and Int8Array instances are bound as blobs. 1154 (TODO: binding the other TypedArray types.) 1155 1156 If passed an array, each element of the array is bound at 1157 the parameter index equal to the array index plus 1 1158 (because arrays are 0-based but binding is 1-based). 1159 1160 If passed an object, each object key is treated as a 1161 bindable parameter name. The object keys _must_ match any 1162 bindable parameter names, including any `$`, `@`, or `:` 1163 prefix. Because `$` is a legal identifier chararacter in 1164 JavaScript, that is the suggested prefix for bindable 1165 parameters: `stmt.bind({$a: 1, $b: 2})`. 1166 1167 It returns this object on success and throws on 1168 error. Errors include: 1169 1170 - Any bind index is out of range, a named bind parameter 1171 does not match, or this statement has no bindable 1172 parameters. 1173 1174 - Any value to bind is of an unsupported type. 1175 1176 - Passed no arguments or more than two. 1177 1178 - The statement has been finalized. 1179 */ 1180 bind: function(/*[ndx,] arg*/){ 1181 affirmStmtOpen(this); 1182 let ndx, arg; 1183 switch(arguments.length){ 1184 case 1: ndx = 1; arg = arguments[0]; break; 1185 case 2: ndx = arguments[0]; arg = arguments[1]; break; 1186 default: toss3("Invalid bind() arguments."); 1187 } 1188 if(undefined===arg){ 1189 /* It might seem intuitive to bind undefined as NULL 1190 but this approach simplifies certain client-side 1191 uses when passing on arguments between 2+ levels of 1192 functions. */ 1193 return this; 1194 }else if(!this.parameterCount){ 1195 toss3("This statement has no bindable parameters."); 1196 } 1197 this._mayGet = false; 1198 if(null===arg){ 1199 /* bind NULL */ 1200 return bindOne(this, ndx, BindTypes.null, arg); 1201 } 1202 else if(Array.isArray(arg)){ 1203 /* bind each entry by index */ 1204 if(1!==arguments.length){ 1205 toss3("When binding an array, an index argument is not permitted."); 1206 } 1207 arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); 1208 return this; 1209 } 1210 else if('object'===typeof arg/*null was checked above*/ 1211 && !util.isBindableTypedArray(arg)){ 1212 /* Treat each property of arg as a named bound parameter. */ 1213 if(1!==arguments.length){ 1214 toss3("When binding an object, an index argument is not permitted."); 1215 } 1216 Object.keys(arg) 1217 .forEach(k=>bindOne(this, k, 1218 affirmSupportedBindType(arg[k]), 1219 arg[k])); 1220 return this; 1221 }else{ 1222 return bindOne(this, ndx, affirmSupportedBindType(arg), arg); 1223 } 1224 toss3("Should not reach this point."); 1225 }, 1226 /** 1227 Special case of bind() which binds the given value using the 1228 BLOB binding mechanism instead of the default selected one for 1229 the value. The ndx may be a numbered or named bind index. The 1230 value must be of type string, null/undefined (both get treated 1231 as null), or a TypedArray of a type supported by the bind() 1232 API. 1233 1234 If passed a single argument, a bind index of 1 is assumed and 1235 the first argument is the value. 1236 */ 1237 bindAsBlob: function(ndx,arg){ 1238 affirmStmtOpen(this); 1239 if(1===arguments.length){ 1240 arg = ndx; 1241 ndx = 1; 1242 } 1243 const t = affirmSupportedBindType(arg); 1244 if(BindTypes.string !== t && BindTypes.blob !== t 1245 && BindTypes.null !== t){ 1246 toss3("Invalid value type for bindAsBlob()"); 1247 } 1248 bindOne(this, ndx, BindTypes.blob, arg); 1249 this._mayGet = false; 1250 return this; 1251 }, 1252 /** 1253 Steps the statement one time. If the result indicates that a 1254 row of data is available, a truthy value is returned. 1255 If no row of data is available, a falsy 1256 value is returned. Throws on error. 1257 */ 1258 step: function(){ 1259 affirmUnlocked(this, 'step()'); 1260 const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); 1261 switch(rc){ 1262 case capi.SQLITE_DONE: return this._mayGet = false; 1263 case capi.SQLITE_ROW: return this._mayGet = true; 1264 default: 1265 this._mayGet = false; 1266 console.warn("sqlite3_step() rc=",rc,"SQL =", 1267 capi.sqlite3_sql(this.pointer)); 1268 DB.checkRc(this.db.pointer, rc); 1269 } 1270 }, 1271 /** 1272 Functions like step() except that 1273 it finalizes this statement immediately after stepping unless 1274 the step cannot be performed because the statement is 1275 locked. Throws on error, but any error other than the 1276 statement-is-locked case will also trigger finalization of this 1277 statement. 1278 1279 On success, it returns true if the step indicated that a row of 1280 data was available, else it returns false. 1281 1282 This is intended to simplify use cases such as: 1283 1284 ``` 1285 aDb.prepare("insert in foo(a) values(?)").bind(123).stepFinalize(); 1286 ``` 1287 */ 1288 stepFinalize: function(){ 1289 affirmUnlocked(this, 'step()'); 1290 const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); 1291 switch(rc){ 1292 case capi.SQLITE_DONE: this.finalize(); return false; 1293 case capi.SQLITE_ROW: this.finalize(); return true; 1294 default: 1295 this.finalize(); 1296 console.warn("sqlite3_step() rc=",rc,"SQL =", 1297 capi.sqlite3_sql(this.pointer)); 1298 DB.checkRc(this.db.pointer, rc); 1299 } 1300 }, 1301 /** 1302 Fetches the value from the given 0-based column index of 1303 the current data row, throwing if index is out of range. 1304 1305 Requires that step() has just returned a truthy value, else 1306 an exception is thrown. 1307 1308 By default it will determine the data type of the result 1309 automatically. If passed a second arugment, it must be one 1310 of the enumeration values for sqlite3 types, which are 1311 defined as members of the sqlite3 module: SQLITE_INTEGER, 1312 SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, 1313 except for undefined, will trigger an exception. Passing 1314 undefined is the same as not passing a value. It is legal 1315 to, e.g., fetch an integer value as a string, in which case 1316 sqlite3 will convert the value to a string. 1317 1318 If ndx is an array, this function behaves a differently: it 1319 assigns the indexes of the array, from 0 to the number of 1320 result columns, to the values of the corresponding column, 1321 and returns that array. 1322 1323 If ndx is a plain object, this function behaves even 1324 differentlier: it assigns the properties of the object to 1325 the values of their corresponding result columns. 1326 1327 Blobs are returned as Uint8Array instances. 1328 1329 Potential TODO: add type ID SQLITE_JSON, which fetches the 1330 result as a string and passes it (if it's not null) to 1331 JSON.parse(), returning the result of that. Until then, 1332 getJSON() can be used for that. 1333 */ 1334 get: function(ndx,asType){ 1335 if(!affirmStmtOpen(this)._mayGet){ 1336 toss3("Stmt.step() has not (recently) returned true."); 1337 } 1338 if(Array.isArray(ndx)){ 1339 let i = 0; 1340 while(i<this.columnCount){ 1341 ndx[i] = this.get(i++); 1342 } 1343 return ndx; 1344 }else if(ndx && 'object'===typeof ndx){ 1345 let i = 0; 1346 while(i<this.columnCount){ 1347 ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++); 1348 } 1349 return ndx; 1350 } 1351 affirmColIndex(this, ndx); 1352 switch(undefined===asType 1353 ? capi.sqlite3_column_type(this.pointer, ndx) 1354 : asType){ 1355 case capi.SQLITE_NULL: return null; 1356 case capi.SQLITE_INTEGER:{ 1357 if(capi.wasm.bigIntEnabled){ 1358 const rc = capi.sqlite3_column_int64(this.pointer, ndx); 1359 if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ 1360 /* Coerce "normal" number ranges to normal number values, 1361 and only return BigInt-type values for numbers out of this 1362 range. */ 1363 return Number(rc).valueOf(); 1364 } 1365 return rc; 1366 }else{ 1367 const rc = capi.sqlite3_column_double(this.pointer, ndx); 1368 if(rc>Number.MAX_SAFE_INTEGER || rc<Number.MIN_SAFE_INTEGER){ 1369 /* Throwing here is arguable but, since we're explicitly 1370 extracting an SQLITE_INTEGER-type value, it seems fair to throw 1371 if the extracted number is out of range for that type. 1372 This policy may be laxened to simply pass on the number and 1373 hope for the best, as the C API would do. */ 1374 toss3("Integer is out of range for JS integer range: "+rc); 1375 } 1376 //console.log("get integer rc=",rc,isInt32(rc)); 1377 return util.isInt32(rc) ? (rc | 0) : rc; 1378 } 1379 } 1380 case capi.SQLITE_FLOAT: 1381 return capi.sqlite3_column_double(this.pointer, ndx); 1382 case capi.SQLITE_TEXT: 1383 return capi.sqlite3_column_text(this.pointer, ndx); 1384 case capi.SQLITE_BLOB: { 1385 const n = capi.sqlite3_column_bytes(this.pointer, ndx), 1386 ptr = capi.sqlite3_column_blob(this.pointer, ndx), 1387 rc = new Uint8Array(n); 1388 //heap = n ? capi.wasm.heap8() : false; 1389 if(n) rc.set(capi.wasm.heap8u().slice(ptr, ptr+n), 0); 1390 //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; 1391 if(n && this.db._blobXfer instanceof Array){ 1392 /* This is an optimization soley for the 1393 Worker-based API. These values will be 1394 transfered to the main thread directly 1395 instead of being copied. */ 1396 this.db._blobXfer.push(rc.buffer); 1397 } 1398 return rc; 1399 } 1400 default: toss3("Don't know how to translate", 1401 "type of result column #"+ndx+"."); 1402 } 1403 abort("Not reached."); 1404 }, 1405 /** Equivalent to get(ndx) but coerces the result to an 1406 integer. */ 1407 getInt: function(ndx){return this.get(ndx,capi.SQLITE_INTEGER)}, 1408 /** Equivalent to get(ndx) but coerces the result to a 1409 float. */ 1410 getFloat: function(ndx){return this.get(ndx,capi.SQLITE_FLOAT)}, 1411 /** Equivalent to get(ndx) but coerces the result to a 1412 string. */ 1413 getString: function(ndx){return this.get(ndx,capi.SQLITE_TEXT)}, 1414 /** Equivalent to get(ndx) but coerces the result to a 1415 Uint8Array. */ 1416 getBlob: function(ndx){return this.get(ndx,capi.SQLITE_BLOB)}, 1417 /** 1418 A convenience wrapper around get() which fetches the value 1419 as a string and then, if it is not null, passes it to 1420 JSON.parse(), returning that result. Throws if parsing 1421 fails. If the result is null, null is returned. An empty 1422 string, on the other hand, will trigger an exception. 1423 */ 1424 getJSON: function(ndx){ 1425 const s = this.get(ndx, capi.SQLITE_STRING); 1426 return null===s ? s : JSON.parse(s); 1427 }, 1428 // Design note: the only reason most of these getters have a 'get' 1429 // prefix is for consistency with getVALUE_TYPE(). The latter 1430 // arguablly really need that prefix for API readability and the 1431 // rest arguably don't, but consistency is a powerful thing. 1432 /** 1433 Returns the result column name of the given index, or 1434 throws if index is out of bounds or this statement has been 1435 finalized. This can be used without having run step() 1436 first. 1437 */ 1438 getColumnName: function(ndx){ 1439 return capi.sqlite3_column_name( 1440 affirmColIndex(affirmStmtOpen(this),ndx).pointer, ndx 1441 ); 1442 }, 1443 /** 1444 If this statement potentially has result columns, this 1445 function returns an array of all such names. If passed an 1446 array, it is used as the target and all names are appended 1447 to it. Returns the target array. Throws if this statement 1448 cannot have result columns. This object's columnCount member 1449 holds the number of columns. 1450 */ 1451 getColumnNames: function(tgt){ 1452 affirmColIndex(affirmStmtOpen(this),0); 1453 if(!tgt) tgt = []; 1454 for(let i = 0; i < this.columnCount; ++i){ 1455 tgt.push(capi.sqlite3_column_name(this.pointer, i)); 1456 } 1457 return tgt; 1458 }, 1459 /** 1460 If this statement has named bindable parameters and the 1461 given name matches one, its 1-based bind index is 1462 returned. If no match is found, 0 is returned. If it has no 1463 bindable parameters, the undefined value is returned. 1464 */ 1465 getParamIndex: function(name){ 1466 return (affirmStmtOpen(this).parameterCount 1467 ? capi.sqlite3_bind_parameter_index(this.pointer, name) 1468 : undefined); 1469 } 1470 }/*Stmt.prototype*/; 1471 1472 {/* Add the `pointer` property to DB and Stmt. */ 1473 const prop = { 1474 enumerable: true, 1475 get: function(){return __ptrMap.get(this)}, 1476 set: ()=>toss3("The pointer property is read-only.") 1477 } 1478 Object.defineProperty(Stmt.prototype, 'pointer', prop); 1479 Object.defineProperty(DB.prototype, 'pointer', prop); 1480 } 1481 1482 /** The OO API's public namespace. */ 1483 sqlite3.oo1 = { 1484 version: { 1485 lib: capi.sqlite3_libversion(), 1486 ooApi: "0.1" 1487 }, 1488 DB, 1489 Stmt 1490 }/*SQLite3 object*/; 1491})(self); 1492