Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Latest upstream sqlite3.js/wasm. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
bd2d5a287f9eb118b52334cd44b7ce10 |
User & Date: | stephan 2022-10-27 06:30:51 |
Context
2022-10-28
| ||
06:26 | Add 'Related Works' section. check-in: 54a018b9f6 user: stephan tags: trunk | |
2022-10-27
| ||
06:30 | Latest upstream sqlite3.js/wasm. check-in: bd2d5a287f user: stephan tags: trunk | |
04:57 | Style tweaks for consistency with the main sqlite.org site. check-in: 1a1933855a user: stephan tags: trunk | |
Changes
Changes to jswasm/sqlite3.js.
︙ | ︙ | |||
24 25 26 27 28 29 30 | ** * May you share freely, never taking more than you give. */ /* ** This code was built from sqlite3 version... ** ** SQLITE_VERSION "3.40.0" ** SQLITE_VERSION_NUMBER 3040000 | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** * May you share freely, never taking more than you give. */ /* ** This code was built from sqlite3 version... ** ** SQLITE_VERSION "3.40.0" ** SQLITE_VERSION_NUMBER 3040000 ** SQLITE_SOURCE_ID "2022-10-27 03:57:48 ed8d3f25a4d6ac04d9f7918c791d8d2c6f23ce846278ca63f8fbadb7ea27alt1" */ var sqlite3InitModule = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; return ( function(sqlite3InitModule) { |
︙ | ︙ | |||
4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 | return (_sqlite3_initialize = Module["_sqlite3_initialize"] = Module["asm"]["sqlite3_initialize"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_vfs_register = Module["_sqlite3_vfs_register"] = function() { return (_sqlite3_vfs_register = Module["_sqlite3_vfs_register"] = Module["asm"]["sqlite3_vfs_register"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_malloc = Module["_sqlite3_malloc"] = function() { return (_sqlite3_malloc = Module["_sqlite3_malloc"] = Module["asm"]["sqlite3_malloc"]).apply(null, arguments); }; /** @type {function(...*):?} */ | > > > > > | 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 | return (_sqlite3_initialize = Module["_sqlite3_initialize"] = Module["asm"]["sqlite3_initialize"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_vfs_register = Module["_sqlite3_vfs_register"] = function() { return (_sqlite3_vfs_register = Module["_sqlite3_vfs_register"] = Module["asm"]["sqlite3_vfs_register"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_vfs_unregister = Module["_sqlite3_vfs_unregister"] = function() { return (_sqlite3_vfs_unregister = Module["_sqlite3_vfs_unregister"] = Module["asm"]["sqlite3_vfs_unregister"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_malloc = Module["_sqlite3_malloc"] = function() { return (_sqlite3_malloc = Module["_sqlite3_malloc"] = Module["asm"]["sqlite3_malloc"]).apply(null, arguments); }; /** @type {function(...*):?} */ |
︙ | ︙ | |||
4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 | return (_sqlite3_realloc = Module["_sqlite3_realloc"] = Module["asm"]["sqlite3_realloc"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_realloc64 = Module["_sqlite3_realloc64"] = function() { return (_sqlite3_realloc64 = Module["_sqlite3_realloc64"] = Module["asm"]["sqlite3_realloc64"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_serialize = Module["_sqlite3_serialize"] = function() { return (_sqlite3_serialize = Module["_sqlite3_serialize"] = Module["asm"]["sqlite3_serialize"]).apply(null, arguments); }; /** @type {function(...*):?} */ | > > > > > | 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 | return (_sqlite3_realloc = Module["_sqlite3_realloc"] = Module["asm"]["sqlite3_realloc"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_realloc64 = Module["_sqlite3_realloc64"] = function() { return (_sqlite3_realloc64 = Module["_sqlite3_realloc64"] = Module["asm"]["sqlite3_realloc64"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_randomness = Module["_sqlite3_randomness"] = function() { return (_sqlite3_randomness = Module["_sqlite3_randomness"] = Module["asm"]["sqlite3_randomness"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_serialize = Module["_sqlite3_serialize"] = function() { return (_sqlite3_serialize = Module["_sqlite3_serialize"] = Module["asm"]["sqlite3_serialize"]).apply(null, arguments); }; /** @type {function(...*):?} */ |
︙ | ︙ | |||
4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 | return (_sqlite3_wasm_enum_json = Module["_sqlite3_wasm_enum_json"] = Module["asm"]["sqlite3_wasm_enum_json"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_wasm_vfs_unlink = Module["_sqlite3_wasm_vfs_unlink"] = function() { return (_sqlite3_wasm_vfs_unlink = Module["_sqlite3_wasm_vfs_unlink"] = Module["asm"]["sqlite3_wasm_vfs_unlink"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_wasm_db_reset = Module["_sqlite3_wasm_db_reset"] = function() { return (_sqlite3_wasm_db_reset = Module["_sqlite3_wasm_db_reset"] = Module["asm"]["sqlite3_wasm_db_reset"]).apply(null, arguments); }; /** @type {function(...*):?} */ | > > > > > | 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 | return (_sqlite3_wasm_enum_json = Module["_sqlite3_wasm_enum_json"] = Module["asm"]["sqlite3_wasm_enum_json"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_wasm_vfs_unlink = Module["_sqlite3_wasm_vfs_unlink"] = function() { return (_sqlite3_wasm_vfs_unlink = Module["_sqlite3_wasm_vfs_unlink"] = Module["asm"]["sqlite3_wasm_vfs_unlink"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_wasm_db_vfs = Module["_sqlite3_wasm_db_vfs"] = function() { return (_sqlite3_wasm_db_vfs = Module["_sqlite3_wasm_db_vfs"] = Module["asm"]["sqlite3_wasm_db_vfs"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _sqlite3_wasm_db_reset = Module["_sqlite3_wasm_db_reset"] = function() { return (_sqlite3_wasm_db_reset = Module["_sqlite3_wasm_db_reset"] = Module["asm"]["sqlite3_wasm_db_reset"]).apply(null, arguments); }; /** @type {function(...*):?} */ |
︙ | ︙ | |||
4717 4718 4719 4720 4721 4722 4723 | ** * May you share freely, never taking more than you give. */ /* ** This code was built from sqlite3 version... ** ** SQLITE_VERSION "3.40.0" ** SQLITE_VERSION_NUMBER 3040000 | | | 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 | ** * May you share freely, never taking more than you give. */ /* ** This code was built from sqlite3 version... ** ** SQLITE_VERSION "3.40.0" ** SQLITE_VERSION_NUMBER 3040000 ** SQLITE_SOURCE_ID "2022-10-27 03:57:48 ed8d3f25a4d6ac04d9f7918c791d8d2c6f23ce846278ca63f8fbadb7ea27alt1" */ /* END FILE: ./bld/sqlite3-license-version.js */ /* BEGIN FILE: api/sqlite3-api-prologue.js */ /* 2022-05-22 The author disclaims copyright to this source code. In place of a |
︙ | ︙ | |||
4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 | }; /** Returns v if v appears to be a TypedArray, else false. */ const isTypedArray = (v)=>{ return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; }; /** Returns true if v appears to be one of our bind()-able TypedArray types: Uint8Array or Int8Array. Support for TypedArrays with element sizes >1 is TODO. */ const isBindableTypedArray = (v)=>{ return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); | > > > > > > > > > > > > > > > > > > > > > > > > > > | 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 | }; /** Returns v if v appears to be a TypedArray, else false. */ const isTypedArray = (v)=>{ return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; }; /** Internal helper to use in operations which need to distinguish between TypedArrays which are backed by a SharedArrayBuffer from those which are not. */ const __SAB = ('undefined'===typeof SharedArrayBuffer) ? function(){} : SharedArrayBuffer; /** Returns true if the given TypedArray object is backed by a SharedArrayBuffer, else false. */ const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB); /** Returns either aTypedArray.slice(begin,end) (if aTypedArray.buffer is a SharedArrayBuffer) or aTypedArray.subarray(begin,end) (if it's not). This distinction is important for APIs which don't like to work on SABs, e.g. TextDecoder, and possibly for our own APIs which work on memory ranges which "might" be modified by other threads while they're working. */ const typedArrayPart = (aTypedArray, begin, end)=>{ return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end); }; /** Returns true if v appears to be one of our bind()-able TypedArray types: Uint8Array or Int8Array. Support for TypedArrays with element sizes >1 is TODO. */ const isBindableTypedArray = (v)=>{ return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); |
︙ | ︙ | |||
4969 4970 4971 4972 4973 4974 4975 | const affirmBindableTypedArray = (v)=>{ return isBindableTypedArray(v) || toss("Value is not of a supported TypedArray type."); }; const utf8Decoder = new TextDecoder('utf-8'); | | | | > | > > > | | < < < < | 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 | const affirmBindableTypedArray = (v)=>{ return isBindableTypedArray(v) || toss("Value is not of a supported TypedArray type."); }; const utf8Decoder = new TextDecoder('utf-8'); /** Uses TextDecoder to decode the given half-open range of the given TypedArray to a string. This differs from a simple call to TextDecoder in that it accounts for whether the first argument is backed by a SharedArrayBuffer or not, and can work more efficiently if it's not (TextDecoder refuses to act upon an SAB). */ const typedArrayToString = function(typedArray, begin, end){ return utf8Decoder.decode(typedArrayPart(typedArray, begin,end)); }; /** If v is-a Array, its join('') result is returned. If isSQLableTypedArray(v) is true then typedArrayToString(v) is returned. Else v is returned as-is. */ |
︙ | ︙ | |||
5003 5004 5005 5006 5007 5008 5009 5010 | */ class WasmAllocError extends Error { constructor(...args){ super(...args); this.name = 'WasmAllocError'; } }; WasmAllocError.toss = (...args)=>{ | > > > > > > > > | | 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 | */ class WasmAllocError extends Error { constructor(...args){ super(...args); this.name = 'WasmAllocError'; } }; /** Functionally equivalent to the WasmAllocError constructor but may be used as part of an expression, e.g.: ``` return someAllocatingFunction(x) || WasmAllocError.toss(...); ``` */ WasmAllocError.toss = (...args)=>{ throw new WasmAllocError(...args); }; /** The main sqlite3 binding API gets installed into this object, mimicking the C API as closely as we can. The numerous members names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as possible, identically to the C-native counterparts, as documented at: |
︙ | ︙ | |||
5217 5218 5219 5220 5221 5222 5223 | stmtPtrPtr,strPtrPtr)=>{}/*installed later*/, /** This binding enables the callback argument to be a JavaScript. If the callback is a function, then for the duration of the sqlite3_exec() call, it installs a WASM-bound function which | | | | | > > > > > > > > > > > > > > > > | | | < > | 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 | stmtPtrPtr,strPtrPtr)=>{}/*installed later*/, /** This binding enables the callback argument to be a JavaScript. If the callback is a function, then for the duration of the sqlite3_exec() call, it installs a WASM-bound function which acts as a proxy for the given callback. That proxy will also perform a conversion of the callback's arguments from `(char**)` to JS arrays of strings. However, for API consistency's sake it will still honor the C-level callback parameter order and will call it like: `callback(pVoid, colCount, listOfValues, listOfColNames)` If the callback is not a JS function then this binding performs no translation of the callback, but the sql argument is still converted to a WASM string for the call using the "flexible-string" argument converter. */ sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/, /** If passed a single argument which appears to be a byte-oriented TypedArray (Int8Array or Uint8Array), this function treats that TypedArray as an output target, fetches `theArray.byteLength` bytes of randomness, and populates the whole array with it. As a special case, if the array's length is 0, this function behaves as if it were passed (0,0). When called this way, it returns its argument, else it returns the `undefined` value. If called with any other arguments, they are passed on as-is to the C API. Results are undefined if passed any incompatible values. */ sqlite3_randomness: (n, outPtr)=>{/*installed later*/}, /** Various internal-use utilities are added here as needed. They are bound to an object only so that we have access to them in the differently-scoped steps of the API bootstrapping process. At the end of the API setup process, this object gets removed. These are NOT part of the public API. */ util:{ affirmBindableTypedArray, flexibleString, bigIntFits32, bigIntFits64, bigIntFitsDouble, isBindableTypedArray, isInt32, isSQLableTypedArray, isTypedArray, typedArrayToString, isUIThread: ()=>'undefined'===typeof WorkerGlobalScope, isSharedTypedArray, typedArrayPart }, /** Holds state which are specific to the WASM-related infrastructure and glue code. It is not expected that client code will normally need these, but they're exposed here in case it does. These APIs are _not_ to be considered an |
︙ | ︙ | |||
5333 5334 5335 5336 5337 5338 5339 | */ dealloc: undefined/*installed later*/ /* Many more wasm-related APIs get installed later on. */ }/*wasm*/ }/*capi*/; | | | 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 | */ dealloc: undefined/*installed later*/ /* Many more wasm-related APIs get installed later on. */ }/*wasm*/ }/*capi*/; const wasm = capi.wasm, util = capi.util; /** wasm.alloc()'s srcTypedArray.byteLength bytes, populates them with the values from the source TypedArray, and returns the pointer to that memory. The returned pointer must eventually be passed to wasm.dealloc() to clean it up. |
︙ | ︙ | |||
5459 5460 5461 5462 5463 5464 5465 | result/argument type strings gets plugged in at a later phase in the API initialization process. */ wasm.bindingSignatures = [ // Please keep these sorted by function name! ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" | | | | | | | | | > | | 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 | result/argument type strings gets plugged in at a later phase in the API initialization process. */ wasm.bindingSignatures = [ // Please keep these sorted by function name! ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" /* TODO: we should arguably write a custom wrapper which knows how to handle Blob, TypedArrays, and JS strings. */ ], ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"], ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int" /* We should arguably create a hand-written binding of bind_text() which does more flexible text conversion, along the lines of sqlite3_prepare_v3(). The slightly problematic part is the final argument (text destructor). */ ], ["sqlite3_close_v2", "int", "sqlite3*"], ["sqlite3_changes", "int", "sqlite3*"], ["sqlite3_clear_bindings","int", "sqlite3_stmt*"], ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"], ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"], ["sqlite3_column_count", "int", "sqlite3_stmt*"], ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"], ["sqlite3_column_int","int", "sqlite3_stmt*", "int"], ["sqlite3_column_name","string", "sqlite3_stmt*", "int"], ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], ["sqlite3_compileoption_get", "string", "int"], ["sqlite3_compileoption_used", "int", "string"], /* sqlite3_create_function(), sqlite3_create_function_v2(), and sqlite3_create_window_function() use hand-written bindings to simplify handling of their function-type arguments. */ ["sqlite3_data_count", "int", "sqlite3_stmt*"], ["sqlite3_db_filename", "string", "sqlite3*", "string"], ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], ["sqlite3_db_name", "string", "sqlite3*", "int"], ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"] /* Careful! Short version: de/serialize() are problematic because they might use a different allocator than the user for managing the deserialized block. de/serialize() are ONLY safe to use with sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */, ["sqlite3_errmsg", "string", "sqlite3*"], ["sqlite3_error_offset", "int", "sqlite3*"], ["sqlite3_errstr", "string", "int"], /*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**" Handled seperately to perform translation of the callback |
︙ | ︙ | |||
5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 | ["sqlite3_libversion_number", "int"], ["sqlite3_malloc", "*","int"], ["sqlite3_open", "int", "string", "*"], ["sqlite3_open_v2", "int", "string", "*", "int", "string"], /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled separately due to us requiring two different sets of semantics for those, depending on how their SQL argument is provided. */ ["sqlite3_realloc", "*","*","int"], ["sqlite3_reset", "int", "sqlite3_stmt*"], ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], ["sqlite3_result_double",undefined, "*", "f64"], ["sqlite3_result_error",undefined, "*", "string", "int"], ["sqlite3_result_error_code", undefined, "*", "int"], ["sqlite3_result_error_nomem", undefined, "*"], | > > | 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 | ["sqlite3_libversion_number", "int"], ["sqlite3_malloc", "*","int"], ["sqlite3_open", "int", "string", "*"], ["sqlite3_open_v2", "int", "string", "*", "int", "string"], /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled separately due to us requiring two different sets of semantics for those, depending on how their SQL argument is provided. */ /* sqlite3_randomness() uses a hand-written wrapper to extend the range of supported argument types. */ ["sqlite3_realloc", "*","*","int"], ["sqlite3_reset", "int", "sqlite3_stmt*"], ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], ["sqlite3_result_double",undefined, "*", "f64"], ["sqlite3_result_error",undefined, "*", "string", "int"], ["sqlite3_result_error_code", undefined, "*", "int"], ["sqlite3_result_error_nomem", undefined, "*"], |
︙ | ︙ | |||
5553 5554 5555 5556 5557 5558 5559 | ["sqlite3_value_blob", "*", "sqlite3_value*"], ["sqlite3_value_bytes","int", "sqlite3_value*"], ["sqlite3_value_double","f64", "sqlite3_value*"], ["sqlite3_value_int","int", "sqlite3_value*"], ["sqlite3_value_text", "string", "sqlite3_value*"], ["sqlite3_value_type", "int", "sqlite3_value*"], ["sqlite3_vfs_find", "*", "string"], | | > | 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 | ["sqlite3_value_blob", "*", "sqlite3_value*"], ["sqlite3_value_bytes","int", "sqlite3_value*"], ["sqlite3_value_double","f64", "sqlite3_value*"], ["sqlite3_value_int","int", "sqlite3_value*"], ["sqlite3_value_text", "string", "sqlite3_value*"], ["sqlite3_value_type", "int", "sqlite3_value*"], ["sqlite3_vfs_find", "*", "string"], ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"], ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"] ]/*wasm.bindingSignatures*/; if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ /* ^^^ "the problem" is that this is an option feature and the build-time function-export list does not currently take optional features into account. */ wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); |
︙ | ︙ | |||
5584 5585 5586 5587 5588 5589 5590 | ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]], ["sqlite3_value_int64","i64", "sqlite3_value*"], ]; /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into | | < < < > > | | 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 | ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]], ["sqlite3_value_int64","i64", "sqlite3_value*"], ]; /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into capi.wasm. */ wasm.bindingSignatures.wasm = [ ["sqlite3_wasm_db_reset", "int", "sqlite3*"], ["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"], ["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"] ]; /** sqlite3.wasm.pstack (pseudo-stack) holds a special-case stack-style allocator intended only for use with _small_ data of not more than (in total) a few kb in size, managed as if it were |
︙ | ︙ | |||
5627 5628 5629 5630 5631 5632 5633 | space managed by Emscripten's stack-management, so does not collide with Emscripten-provided stack allocation APIs. The memory lives in the WASM heap and can be used with routines such as wasm.setMemValue() and any wasm.heap8u().slice(). */ wasm.pstack = Object.assign(Object.create(null),{ /** | | | | | | 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 | space managed by Emscripten's stack-management, so does not collide with Emscripten-provided stack allocation APIs. The memory lives in the WASM heap and can be used with routines such as wasm.setMemValue() and any wasm.heap8u().slice(). */ wasm.pstack = Object.assign(Object.create(null),{ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from this.pointer. */ restore: wasm.exports.sqlite3_wasm_pstack_restore, /** Attempts to allocate the given number of bytes from the pstack. On success, it zeroes out a block of memory of the given size, adjusts the pstack pointer, and returns a pointer to the memory. On error, returns throws a WasmAllocError. The memory must eventually be released using restore(). This method always adjusts the given value to be a multiple of 8 bytes because failing to do so can lead to incorrect results when reading and writing 64-bit values from/to the WASM heap. Similarly, the returned address is always 8-byte aligned. */ alloc: (n)=>{ return wasm.exports.sqlite3_wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); }, /** alloc()'s n chunks, each sz bytes, as a single memory block and returns the addresses as an array of n element, each holding the address of one chunk. Throws a WasmAllocError if allocation fails. Example: |
︙ | ︙ | |||
5688 5689 5690 5691 5692 5693 5694 | double or int64, and that value must be written or fetched, e.g. using wasm.setMemValue() or wasm.getMemValue(), it is important that the pointer in question be aligned to an 8-byte boundary or else it will not be fetched or written properly and will corrupt or read neighboring memory. However, when all pointers involved point to "small" data, it | | | 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 | double or int64, and that value must be written or fetched, e.g. using wasm.setMemValue() or wasm.getMemValue(), it is important that the pointer in question be aligned to an 8-byte boundary or else it will not be fetched or written properly and will corrupt or read neighboring memory. However, when all pointers involved point to "small" data, it is safe to pass a falsy value to save a tiny bit of memory. */ allocPtr: (n=1,safePtrSize=true)=>{ return 1===n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); } })/*wasm.pstack*/; |
︙ | ︙ | |||
5741 5742 5743 5744 5745 5746 5747 | The C-level APIs never throw, but some of the higher-level C-style APIs do and the object-oriented APIs use exceptions exclusively to report errors. */ class SQLite3Error extends Error { /** Constructs this object with a message equal to all arguments | | > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | < < < < | | 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 | The C-level APIs never throw, but some of the higher-level C-style APIs do and the object-oriented APIs use exceptions exclusively to report errors. */ class SQLite3Error extends Error { /** Constructs this object with a message equal to all arguments concatenated with a space between each one. As a special case, if it's passed only a single integer argument, the string form of that argument is the result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns falsy), a synthesized string which contains that integer. */ constructor(...args){ if(1===args.length && 'number'===typeof args[0] && args[0]===(args[0] | 0)){ super(capi.sqlite3_js_rc_str(args[0]) || ("Unknown result code #"+args[0])); }else{ super(args.join(' ')); } this.name = 'SQLite3Error'; } }; /** Functionally equivalent to the SQLite3Error constructor but may be used as part of an expression, e.g.: ``` return someFunction(x) || SQLite3Error.toss(...); ``` */ SQLite3Error.toss = (...args)=>{ throw new SQLite3Error(...args); }; capi.sqlite3_randomness = (...args)=>{ if(1===args.length && util.isTypedArray(args[0]) && 1===args[0].BYTES_PER_ELEMENT){ const ta = args[0]; if(0===ta.byteLength){ wasm.exports.sqlite3_randomness(0,0); return ta; } const stack = wasm.pstack.pointer; try { let n = ta.byteLength, offset = 0; const r = wasm.exports.sqlite3_randomness; const heap = wasm.heap8u(); const nAlloc = n < 512 ? n : 512; const ptr = wasm.pstack.alloc(nAlloc); do{ const j = (n>nAlloc ? nAlloc : n); r(j, ptr); ta.set(typedArrayPart(heap, ptr, ptr+j), offset); n -= j; offset += j; } while(n > 0); }catch(e){ console.error("Highly unexpected (and ignored!) "+ "exception in sqlite3_randomness():",e); }finally{ wasm.pstack.restore(stack); } return ta; } capi.wasm.exports.sqlite3_randomness(...args); }; /** State for sqlite3_wasmfs_opfs_dir(). */ let __wasmfsOpfsDir = undefined; /** If the wasm environment has a WASMFS/OPFS-backed persistent storage directory, its path is returned by this function. If it does not then it returns "" (noting that "" is a falsy value). The first time this is called, this function inspects the current environment to determine whether persistence support is available and, if it is, enables it (if needed). This function currently only recognizes the WASMFS/OPFS storage combination and its path refers to storage rooted in the Emscripten-managed virtual filesystem. */ capi.sqlite3_wasmfs_opfs_dir = function(){ if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir; // If we have no OPFS, there is no persistent dir const pdir = config.wasmfsOpfsDir; if(!pdir || !self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle){ return __wasmfsOpfsDir = ""; } try{ if(pdir && 0===wasm.xCallWrapped( 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ return __wasmfsOpfsDir = pdir; }else{ return __wasmfsOpfsDir = ""; } }catch(e){ // sqlite3_wasm_init_wasmfs() is not available return __wasmfsOpfsDir = ""; } }; /** Experimental and subject to change or removal. Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a non-empty string and the given name starts with (that string + '/'), else returns false. */ capi.sqlite3_wasmfs_filename_is_persistent = function(name){ const p = capi.sqlite3_wasmfs_opfs_dir(); return (p && name) ? name.startsWith(p+'/') : false; }; // This bit is highly arguable and is incompatible with the fiddle shell. if(false && 0===wasm.exports.sqlite3_vfs_find(0)){ /* Assume that sqlite3_initialize() has not yet been called. |
︙ | ︙ | |||
5824 5825 5826 5827 5828 5829 5830 | that VFS, else returns false. If pDb is falsy then the 3rd argument is ignored and this function returns a truthy value if the default VFS name matches that of the 2nd argument. Results are undefined if pDb is truthy but refers to an invalid pointer. The 3rd argument specifies the database name of the given database connection to check, defaulting to the main db. | | | > > | | | < < < < < < | < < | | | < < < | > > > > > > > > > > > > > > > > > > > > > > > | | | 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 | that VFS, else returns false. If pDb is falsy then the 3rd argument is ignored and this function returns a truthy value if the default VFS name matches that of the 2nd argument. Results are undefined if pDb is truthy but refers to an invalid pointer. The 3rd argument specifies the database name of the given database connection to check, defaulting to the main db. The 2nd and 3rd arguments may either be a JS string or a WASM C-string. If the 2nd argument is a NULL WASM pointer, the default VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is assumed. The truthy value it returns is a pointer to the `sqlite3_vfs` object. To permit safe use of this function from APIs which may be called via the C stack (like SQL UDFs), this function does not throw: if bad arguments cause a conversion error when passing into wasm-space, false is returned. */ capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName="main"){ try{ const pK = capi.sqlite3_vfs_find(vfsName); if(!pK) return false; else if(!pDb){ return pK===capi.sqlite3_vfs_find(0) ? pK : false; }else{ return pK===capi.sqlite3_js_db_vfs(pDb) ? pK : false; } }catch(e){ /* Ignore - probably bad args to a wasm-bound function. */ return false; } }; /** Returns an array of the names of all currently-registered sqlite3 VFSes. */ capi.sqlite3_js_vfs_list = function(){ const rc = []; let pVfs = capi.sqlite3_vfs_find(0); while(pVfs){ const oVfs = new capi.sqlite3_vfs(pVfs); rc.push(wasm.cstringToJs(oVfs.$zName)); pVfs = oVfs.$pNext; oVfs.dispose(); } return rc; }; /** Serializes the given `sqlite3*` pointer to a Uint8Array, as per sqlite3_serialize(). On success it returns a Uint8Array. On error it throws with a description of the problem. */ capi.sqlite3_js_db_export = function(pDb){ if(!pDb) toss('Invalid sqlite3* argument.'); if(!wasm.bigIntEnabled) toss('BigInt64 support is not enabled.'); const stack = wasm.pstack.pointer; let pOut; try{ const pSize = wasm.pstack.alloc(8/*i64*/ + wasm.ptrSizeof); const ppOut = pSize + 8; /** Maintenance reminder, since this cost a full hour of grief and confusion: if the order of pSize/ppOut are reversed in that memory block, fetching the value of pSize after the export reads a garbage size because it's not on an 8-byte memory boundary! */ let rc = wasm.exports.sqlite3_wasm_db_serialize( pDb, ppOut, pSize, 0 ); if(rc){ toss("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); } pOut = wasm.getPtrValue(ppOut); const nOut = wasm.getMemValue(pSize, 'i64'); rc = nOut ? wasm.heap8u().slice(pOut, pOut + Number(nOut)) : new Uint8Array(); return rc; }finally{ if(pOut) wasm.exports.sqlite3_free(pOut); wasm.pstack.restore(stack); } }; /** Given a `sqlite3*` and a database name (JS string or WASM C-string pointer, which may be 0), returns a pointer to the sqlite3_vfs responsible for it. If the given db name is null/0, or not provided, then "main" is assumed. */ capi.sqlite3_js_db_vfs = (dbPointer, dbName=0)=>wasm.sqlite3_wasm_db_vfs(dbPointer, dbName); /** A thin wrapper around capi.sqlite3_aggregate_context() which behaves the same except that it throws a WasmAllocError if that function returns 0. As a special case, if n is falsy it does _not_ throw if that function returns 0. That special case is intended for use with xFinal() implementations. */ capi.sqlite3_js_aggregate_context = (pCtx, n)=>{ return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate",n, "bytes for sqlite3_aggregate_context()") : 0); }; if( capi.util.isUIThread() ){ /* Features specific to the main window thread... */ /** Internal helper for sqlite3_js_kvvfs_clear() and friends. Its argument should be one of ('local','session',''). */ const __kvvfsInfo = function(which){ const rc = Object.create(null); rc.prefix = 'kvvfs-'+which; rc.stores = []; if('session'===which || ''===which) rc.stores.push(self.sessionStorage); |
︙ | ︙ | |||
5945 5946 5947 5948 5949 5950 5951 | the pattern used by kvvfs are cleared: any other client-side data are retained. This function is only available in the main window thread. Returns the number of entries cleared. */ | | | 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 | the pattern used by kvvfs are cleared: any other client-side data are retained. This function is only available in the main window thread. Returns the number of entries cleared. */ capi.sqlite3_js_kvvfs_clear = function(which=''){ let rc = 0; const kvinfo = __kvvfsInfo(which); kvinfo.stores.forEach((s)=>{ const toRm = [] /* keys to remove */; let i; for( i = 0; i < s.length; ++i ){ const k = s.key(i); |
︙ | ︙ | |||
5978 5979 5980 5981 5982 5983 5984 | Note that the returned size is not authoritative from the perspective of how much data can fit into localStorage and sessionStorage, as the precise algorithms for determining those limits are unspecified and may include per-entry overhead invisible to clients. */ | | | 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 | Note that the returned size is not authoritative from the perspective of how much data can fit into localStorage and sessionStorage, as the precise algorithms for determining those limits are unspecified and may include per-entry overhead invisible to clients. */ capi.sqlite3_js_kvvfs_size = function(which=''){ let sz = 0; const kvinfo = __kvvfsInfo(which); kvinfo.stores.forEach((s)=>{ let i; for(i = 0; i < s.length; ++i){ const k = s.key(i); if(k.startsWith(kvinfo.prefix)){ |
︙ | ︙ | |||
6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 | pointer-to-pointer values. */ target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR); /** Convenience form of setMemValue() intended for setting pointer-to-pointer values. */ target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR); /** Expects ptr to be a pointer into the WASM heap memory which refers to a NUL-terminated C-style string encoded as UTF-8. Returns the length, in bytes, of the string, as for `strlen(3)`. As a special case, if !ptr then it it returns `null`. Throws if ptr is out of range for target.heap8u(). */ | > > > > > > > > > > > > > > > > > | 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 | pointer-to-pointer values. */ target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR); /** Convenience form of setMemValue() intended for setting pointer-to-pointer values. */ target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR); /** Returns true if the given value appears to be legal for use as a WASM pointer value. Its _range_ of values is not (cannot be) validated except to ensure that it is a 32-bit integer with a value of 0 or greater. Likewise, it cannot verify whether the value actually refers to allocated memory in the WASM heap. */ target.isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0); /** isPtr() is an alias for isPtr32(). If/when 64-bit WASM pointer support becomes widespread, it will become an alias for either isPtr32() or the as-yet-hypothetical isPtr64(), depending on a configuration option. */ target.isPtr = target.isPtr32; /** Expects ptr to be a pointer into the WASM heap memory which refers to a NUL-terminated C-style string encoded as UTF-8. Returns the length, in bytes, of the string, as for `strlen(3)`. As a special case, if !ptr then it it returns `null`. Throws if ptr is out of range for target.heap8u(). */ |
︙ | ︙ | |||
7380 7381 7382 7383 7384 7385 7386 | xcv.arg.i8 = (i)=>((i | 0) & 0xFF); xcv.arg.f32 = xcv.arg.float = (i)=>Number(i).valueOf(); xcv.arg.f64 = xcv.arg.double = xcv.arg.f32; xcv.arg.int = xcv.arg.i32; xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR]; xcv.result['number'] = (v)=>Number(v); | | > | 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 | xcv.arg.i8 = (i)=>((i | 0) & 0xFF); xcv.arg.f32 = xcv.arg.float = (i)=>Number(i).valueOf(); xcv.arg.f64 = xcv.arg.double = xcv.arg.f32; xcv.arg.int = xcv.arg.i32; xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR]; xcv.result['number'] = (v)=>Number(v); { /* Copy certain xcv.arg[...] handlers to xcv.result[...] and add pointer-style variants of them. */ const copyToResult = ['i8', 'i16', 'i32', 'int', 'f32', 'float', 'f64', 'double']; if(target.bigIntEnabled) copyToResult.push('i64'); for(const t of copyToResult){ xcv.arg[t+'*'] = xcv.result[t+'*'] = xcv.arg[ptrIR]; xcv.result[t] = xcv.arg[t] || toss("Missing arg converter:",t); } |
︙ | ︙ | |||
8634 8635 8636 8637 8638 8639 8640 | /* "The problem" is that the following isn't even remotely type-safe. OTOH, nothing about WASM pointers is. */ const argPointer = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('StructType', (v)=>{ if(v && v.constructor && v instanceof StructBinder.StructType){ v = v.pointer; } | | | 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 | /* "The problem" is that the following isn't even remotely type-safe. OTOH, nothing about WASM pointers is. */ const argPointer = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('StructType', (v)=>{ if(v && v.constructor && v instanceof StructBinder.StructType){ v = v.pointer; } return wasm.isPtr(v) ? argPointer(v) : toss("Invalid (object) type for StructType-type argument."); }); } if(1){/* Convert Arrays and certain TypedArrays to strings for 'flexible-string'-type arguments */ |
︙ | ︙ | |||
8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 | `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const aPtr = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('sqlite3*', aPtr) ('sqlite3_stmt*', aPtr) ('sqlite3_context*', aPtr) ('sqlite3_value*', aPtr) ('void*', aPtr); wasm.xWrap.resultAdapter('sqlite3*', aPtr) ('sqlite3_stmt*', aPtr) | > > | | 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 | `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const aPtr = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('sqlite3*', aPtr) ('sqlite3_stmt*', aPtr) ('sqlite3_context*', aPtr) ('sqlite3_value*', aPtr) ('sqlite3_vfs*', aPtr) ('void*', aPtr); wasm.xWrap.resultAdapter('sqlite3*', aPtr) ('sqlite3_context*', aPtr) ('sqlite3_stmt*', aPtr) ('sqlite3_vfs*', aPtr) ('void*', aPtr); /** Populate api object with sqlite3_...() by binding the "raw" wasm exports into type-converting proxies using wasm.xWrap(). */ for(const e of wasm.bindingSignatures){ |
︙ | ︙ | |||
8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 | "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xInverse*/, "*"/*xDestroy*/] ); const __udfSetResult = function(pCtx, val){ //console.warn("udfSetResult",typeof val, val); switch(typeof val) { case 'boolean': capi.sqlite3_result_int(pCtx, val ? 1 : 0); break; case 'bigint': if(wasm.bigIntEnabled){ if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); else toss3("BigInt value",val.toString(),"is too BigInt for int64."); | > > > | 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 | "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xInverse*/, "*"/*xDestroy*/] ); const __udfSetResult = function(pCtx, val){ //console.warn("udfSetResult",typeof val, val); switch(typeof val) { case 'undefined': /* Assume that the client already called sqlite3_result_xxx(). */ break; case 'boolean': capi.sqlite3_result_int(pCtx, val ? 1 : 0); break; case 'bigint': if(wasm.bigIntEnabled){ if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); else toss3("BigInt value",val.toString(),"is too BigInt for int64."); |
︙ | ︙ | |||
8907 8908 8909 8910 8911 8912 8913 | return tgt; }/*__udfConvertArgs()*/; const __udfSetError = (pCtx, e)=>{ if(e instanceof sqlite3.WasmAllocError){ capi.sqlite3_result_error_nomem(pCtx); }else{ | > | | 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 | return tgt; }/*__udfConvertArgs()*/; const __udfSetError = (pCtx, e)=>{ if(e instanceof sqlite3.WasmAllocError){ capi.sqlite3_result_error_nomem(pCtx); }else{ const msg = ('string'===typeof e) ? e : e.message; capi.sqlite3_result_error(pCtx, msg, -1); } }; const __xFunc = function(callback){ return function(pCtx, argc, pArgv){ try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) } catch(e){ |
︙ | ︙ | |||
9026 9027 9028 9029 9030 9031 9032 | /* Wrap the callbacks in a WASM-bound functions... */ const wasm = capi.wasm; const uninstall = [/*funcs to uninstall on error*/]; let rc; try{ const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, uninstall); | | | | > > > > > | 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 | /* Wrap the callbacks in a WASM-bound functions... */ const wasm = capi.wasm; const uninstall = [/*funcs to uninstall on error*/]; let rc; try{ const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, uninstall); rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, ...funcArgs); }catch(e){ console.error("sqlite3_create_window_function() setup threw:",e); for(let v of uninstall){ wasm.uninstallFunction(v); } rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, "Creation of UDF threw: "+e.message); } return rc; }; /** A helper for UDFs implemented in JS and bound to WASM by the client. Given a JS value, udfSetResult(pCtx,X) calls one of the sqlite3_result_xyz(pCtx,...) routines, depending on X's data type: - `null`: sqlite3_result_null() - `boolean`: sqlite3_result_int() - `number`: sqlite3_result_int() or sqlite3_result_double() - `string`: sqlite3_result_text() - Uint8Array or Int8Array: sqlite3_result_blob() - `undefined`: indicates that the UDF called one of the `sqlite3_result_xyz()` routines on its own, making this function a no-op. Results are _undefined_ if this function is passed the `undefined` value but did _not_ call one of the `sqlite3_result_xyz()` routines. Anything else triggers sqlite3_result_error(). */ capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_create_window_function.udfSetResult = __udfSetResult; |
︙ | ︙ | |||
9081 9082 9083 9084 9085 9086 9087 | capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs; /** A helper for UDFs implemented in JS and bound to WASM by the client. It expects to be a passed `(sqlite3_context*, Error)` | | | | | | 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 | capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs; /** A helper for UDFs implemented in JS and bound to WASM by the client. It expects to be a passed `(sqlite3_context*, Error)` (an exception object or message string). And it sets the current UDF's result to sqlite3_result_error_nomem() or sqlite3_result_error(), depending on whether the 2nd argument is a sqlite3.WasmAllocError object or not. */ capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_create_window_function.udfSetError = __udfSetError; }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; |
︙ | ︙ | |||
9179 9180 9181 9182 9183 9184 9185 | __rcMap[e[1]] = e[0]; } } /** For the given integer, returns the SQLITE_xxx result code as a string, or undefined if no such mapping is found. */ | | > > > > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > | | | 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 | __rcMap[e[1]] = e[0]; } } /** For the given integer, returns the SQLITE_xxx result code as a string, or undefined if no such mapping is found. */ capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc]; /* Bind all registered C-side structs... */ const notThese = Object.assign(Object.create(null),{ // Structs NOT to register WasmTestStruct: true }); if(!util.isUIThread()){ /* We remove the kvvfs VFS from Worker threads below. */ notThese.sqlite3_kvvfs_methods = true; } for(const s of wasm.ctype.structs){ if(!notThese[s.name]){ capi[s.name] = sqlite3.StructBinder(s); } } }/*end C constant imports*/ const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); if( pKvvfs ){/* kvvfs-specific glue */ if(util.isUIThread()){ const kvvfsMethods = new capi.sqlite3_kvvfs_methods( wasm.exports.sqlite3_wasm_kvvfs_methods() ); delete capi.sqlite3_kvvfs_methods; const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack, pstack = wasm.pstack, pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc; const kvvfsStorage = (zClass)=> ((115/*=='s'*/===wasm.getMemValue(zClass)) ? sessionStorage : localStorage); const kvvfsImpls = { xRead: (zClass, zKey, zBuf, nBuf)=>{ const stack = pstack.pointer, astack = wasm.scopedAllocPush(); try { const zXKey = kvvfsMakeKey(zClass,zKey); if(!zXKey) return -3/*OOM*/; const jKey = wasm.cstringToJs(zXKey); const jV = kvvfsStorage(zClass).getItem(jKey); if(!jV) return -1; const nV = jV.length /* Note that we are relying 100% on v being ASCII so that jV.length is equal to the C-string's byte length. */; if(nBuf<=0) return nV; else if(1===nBuf){ wasm.setMemValue(zBuf, 0); return nV; } const zV = wasm.scopedAllocCString(jV); if(nBuf > nV + 1) nBuf = nV + 1; wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1); wasm.setMemValue(zBuf + nBuf - 1, 0); return nBuf - 1; }catch(e){ console.error("kvstorageRead()",e); return -2; }finally{ pstack.restore(stack); wasm.scopedAllocPop(astack); } }, xWrite: (zClass, zKey, zData)=>{ const stack = pstack.pointer; try { const zXKey = kvvfsMakeKey(zClass,zKey); if(!zXKey) return 1/*OOM*/; const jKey = wasm.cstringToJs(zXKey); kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData)); return 0; }catch(e){ console.error("kvstorageWrite()",e); return capi.SQLITE_IOERR; }finally{ pstack.restore(stack); } }, xDelete: (zClass, zKey)=>{ const stack = pstack.pointer; try { const zXKey = kvvfsMakeKey(zClass,zKey); if(!zXKey) return 1/*OOM*/; kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey)); return 0; }catch(e){ console.error("kvstorageDelete()",e); return capi.SQLITE_IOERR; }finally{ pstack.restore(stack); } } }/*kvvfsImpls*/; for(let k of Object.keys(kvvfsImpls)){ kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction( kvvfsMethods.memberSignature(k), kvvfsImpls[k] ); } }else{ /* Worker thread: unregister kvvfs to avoid it being used for anything other than local/sessionStorage. It "can" be used that way but it's not really intended to be. */ capi.sqlite3_vfs_unregister(pKvvfs); } }/*pKvvfs*/ }); /* END FILE: api/sqlite3-api-glue.js */ /* BEGIN FILE: ./bld/sqlite3-api-build-version.js */ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.version = {"libVersion": "3.40.0", "libVersionNumber": 3040000, "sourceId": "2022-10-27 03:57:48 ed8d3f25a4d6ac04d9f7918c791d8d2c6f23ce846278ca63f8fbadb7ea27alt1","downloadVersion": 3400000}; }); /* END FILE: ./bld/sqlite3-api-build-version.js */ /* BEGIN FILE: api/sqlite3-api-oo1.js */ /* 2022-07-22 The author disclaims copyright to this source code. In place of a |
︙ | ︙ | |||
9374 9375 9376 9377 9378 9379 9380 | const __vfsPostOpenSql = Object.create(null); /** A proxy for DB class constructors. It must be called with the being-construct DB object as its "this". See the DB constructor for the argument docs. This is split into a separate function in order to enable simple creation of special-case DB constructors, | | | 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 | const __vfsPostOpenSql = Object.create(null); /** A proxy for DB class constructors. It must be called with the being-construct DB object as its "this". See the DB constructor for the argument docs. This is split into a separate function in order to enable simple creation of special-case DB constructors, e.g. JsStorageDb and OpfsDb. Expects to be passed a configuration object with the following properties: - `.filename`: the db filename. It may be a special name like ":memory:" or "". |
︙ | ︙ | |||
9436 9437 9438 9439 9440 9441 9442 | let pDb, oflags = 0; if( flagsStr.indexOf('c')>=0 ){ oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; } if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; oflags |= capi.SQLITE_OPEN_EXRESCODE; | | | < < < | < | > | < | | | | < | | 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 | let pDb, oflags = 0; if( flagsStr.indexOf('c')>=0 ){ oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; } if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; oflags |= capi.SQLITE_OPEN_EXRESCODE; const stack = wasm.pstack.pointer; try { const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */; let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0); pDb = wasm.getPtrValue(pPtr); checkSqlite3Rc(pDb, rc); if(flagsStr.indexOf('t')>=0){ capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, 0); } // Check for per-VFS post-open SQL... const pVfs = capi.sqlite3_js_db_vfs(pDb); //console.warn("Opened db",fn,"with vfs",vfsName,pVfs); if(!pVfs) toss3("Internal error: cannot get VFS for new db handle."); const postInitSql = __vfsPostOpenSql[pVfs]; if(postInitSql){ rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0); checkSqlite3Rc(pDb, rc); } }catch( e ){ if( pDb ) capi.sqlite3_close_v2(pDb); throw e; }finally{ wasm.pstack.restore(stack); } this.filename = fnJs; __ptrMap.set(this, pDb); __stmtMap.set(this, Object.create(null)); }; /** |
︙ | ︙ | |||
9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 | */ const parseExecArgs = function(args){ const out = Object.create(null); out.opt = Object.create(null); switch(args.length){ case 1: if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){ out.sql = args[0]; }else if(args[0] && 'object'===typeof args[0]){ out.opt = args[0]; out.sql = out.opt.sql; | > > < < | | 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 | */ const parseExecArgs = function(args){ const out = Object.create(null); out.opt = Object.create(null); switch(args.length){ case 1: if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){ out.sql = args[0]; }else if(Array.isArray(args[0])){ out.sql = args[0]; }else if(args[0] && 'object'===typeof args[0]){ out.opt = args[0]; out.sql = out.opt.sql; } break; case 2: out.sql = args[0]; out.opt = args[1]; break; default: toss3("Invalid argument count for exec()."); }; if(util.isSQLableTypedArray(out.sql)){ out.sql = util.typedArrayToString(out.sql); }else if(Array.isArray(out.sql)){ out.sql = out.sql.join(''); }else if('string'!==typeof out.sql){ toss3("Missing SQL argument or unsupported SQL value type."); } if(out.opt.callback || out.opt.resultRows){ switch((undefined===out.opt.rowMode) ? 'array' : out.opt.rowMode) { case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break; case 'array': out.cbArg = (stmt)=>stmt.get([]); break; case 'stmt': |
︙ | ︙ | |||
9807 9808 9809 9810 9811 9812 9813 | }else{ return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); } }, /** | | | < < | | | < < < < < < < < < < | 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 | }else{ return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); } }, /** Similar to the this.filename but returns the sqlite3_db_filename() value for the given database name, defaulting to "main". The argument may be either a JS string or a pointer to a WASM-allocated C-string. */ dbFilename: function(dbName='main'){ return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); }, /** Returns the name of the given 0-based db number, as documented for sqlite3_db_name(). */ dbName: function(dbNumber=0){ return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); |
︙ | ︙ | |||
10100 10101 10102 10103 10104 10105 10106 | In the final two cases, the function must be defined as the `callback` property of the options object (optionally called `xFunc` to align with the C API documentation). In the final case, the function's name must be the 'name' property. The first two call forms can only be used for creating scalar | | | | 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 | In the final two cases, the function must be defined as the `callback` property of the options object (optionally called `xFunc` to align with the C API documentation). In the final case, the function's name must be the 'name' property. The first two call forms can only be used for creating scalar functions. Creating an aggregate or window function requires the options-object form (see below for details). UDFs cannot currently be removed from a DB handle after they're added. More correctly, they can be removed as documented for sqlite3_create_function_v2(), but doing so will "leak" the JS-created WASM binding of those functions. On success, returns this object. Throws on error. |
︙ | ︙ | |||
10126 10127 10128 10129 10130 10131 10132 10133 | - Scalar: set the `xFunc` function-type property to the UDF function. - Aggregate: set the `xStep` and `xFinal` function-type properties to the "step" and "final" callbacks for the aggregate. Do not set the `xFunc` property. The options object may optionally have an `xDestroy` | > > > | < | | | > > > | < < | | | | | > | | | > > > > > < | | | | > > > > | | > > > > > > > > > > > > | < < | | | > > > > | > > > > > | > > | 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 | - Scalar: set the `xFunc` function-type property to the UDF function. - Aggregate: set the `xStep` and `xFinal` function-type properties to the "step" and "final" callbacks for the aggregate. Do not set the `xFunc` property. - Window: set the `xStep`, `xFinal`, `xValue`, and `xInverse` function-type properties. Do not set the `xFunc` property. The options object may optionally have an `xDestroy` function-type property, as per sqlite3_create_function_v2(). Its argument will be the WASM-pointer-type value of the `pApp` property, and this function will throw if `pApp` is defined but is not null, undefined, or a numeric (WASM pointer) value. i.e. `pApp`, if set, must be value suitable for use as a WASM pointer argument, noting that `null` or `undefined` will translate to 0 for that purpose. The options object may contain flags to modify how the function is defined: - `arity`: the number of arguments which SQL calls to this function expect or require. The default value is `xFunc.length` or `xStep.length` (i.e. the number of declared parameters it has) **MINUS 1** (see below for why). As a special case, if the `length` is 0, its arity is also 0 instead of -1. A negative arity value means that the function is variadic and may accept any number of arguments, up to sqlite3's compile-time limits. sqlite3 will enforce the argument count if is zero or greater. The callback always receives a pointer to an `sqlite3_context` object as its first argument. Any arguments after that are from SQL code. The leading context argument does _not_ count towards the function's arity. See the docs for sqlite3.capi.sqlite3_create_function_v2() for why that argument is needed in the interface. The following options-object properties correspond to flags documented at: https://sqlite.org/c3ref/create_function.html - `deterministic` = sqlite3.capi.SQLITE_DETERMINISTIC - `directOnly` = sqlite3.capi.SQLITE_DIRECTONLY - `innocuous` = sqlite3.capi.SQLITE_INNOCUOUS Sidebar: the ability to add new WASM-accessible functions to the runtime requires that the WASM build is compiled with the equivalent functionality as that provided by Emscripten's `-sALLOW_TABLE_GROWTH` flag. */ createFunction: function f(name, xFunc, opt){ const isFunc = (f)=>(f instanceof Function); switch(arguments.length){ case 1: /* (optionsObject) */ opt = name; name = opt.name; xFunc = opt.xFunc || 0; break; case 2: /* (name, callback|optionsObject) */ if(!isFunc(xFunc)){ opt = xFunc; xFunc = opt.xFunc || 0; } break; case 3: /* name, xFunc, opt */ break; default: break; } if(!opt) opt = {}; if('string' !== typeof name){ toss3("Invalid arguments: missing function name."); } let xStep = opt.xStep || 0; let xFinal = opt.xFinal || 0; const xValue = opt.xValue || 0; const xInverse = opt.xInverse || 0; let isWindow = undefined; if(isFunc(xFunc)){ isWindow = false; if(isFunc(xStep) || isFunc(xFinal)){ toss3("Ambiguous arguments: scalar or aggregate?"); } xStep = xFinal = null; }else if(isFunc(xStep)){ if(!isFunc(xFinal)){ toss3("Missing xFinal() callback for aggregate or window UDF."); } xFunc = null; }else if(isFunc(xFinal)){ toss3("Missing xStep() callback for aggregate or window UDF."); }else{ toss3("Missing function-type properties."); } if(false === isWindow){ if(isFunc(xValue) || isFunc(xInverse)){ toss3("xValue and xInverse are not permitted for non-window UDFs."); } }else if(isFunc(xValue)){ if(!isFunc(xInverse)){ toss3("xInverse must be provided if xValue is."); } isWindow = true; }else if(isFunc(xInverse)){ toss3("xValue must be provided if xInverse is."); } const pApp = opt.pApp; if(undefined!==pApp && null!==pApp && (('number'!==typeof pApp) || !capi.util.isInt32(pApp))){ toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); } const xDestroy = opt.xDestroy || 0; if(xDestroy && !isFunc(xDestroy)){ toss3("xDestroy property must be a function."); } let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS; name = name.toLowerCase(); const xArity = xFunc || xStep; const arity = getOwnOption(opt, 'arity'); const arityArg = ('number'===typeof arity ? arity : (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0)); let rc; if( isWindow ){ rc = capi.sqlite3_create_window_function( this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy); }else{ rc = capi.sqlite3_create_function_v2( this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy); } DB.checkRc(this, rc); return this; }/*createFunction()*/, /** Prepares the given SQL, step()s it one time, and returns the value of the first result column. If it has no results, undefined is returned. |
︙ | ︙ | |||
10693 10694 10695 10696 10697 10698 10699 | const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); switch(rc){ case capi.SQLITE_DONE: return this._mayGet = false; case capi.SQLITE_ROW: return this._mayGet = true; default: this._mayGet = false; console.warn("sqlite3_step() rc=",rc, | | | 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 | const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); switch(rc){ case capi.SQLITE_DONE: return this._mayGet = false; case capi.SQLITE_ROW: return this._mayGet = true; default: this._mayGet = false; console.warn("sqlite3_step() rc=",rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer)); DB.checkRc(this.db.pointer, rc); } }, /** Functions exactly like step() except that... |
︙ | ︙ | |||
10931 10932 10933 10934 10935 10936 10937 | ooApi: "0.1" }, DB, Stmt, dbCtorHelper }/*oo1 object*/; | | | | | | | 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 | ooApi: "0.1" }, DB, Stmt, dbCtorHelper }/*oo1 object*/; if(util.isUIThread()){ /** Functionally equivalent to DB(storageName,'c','kvvfs') except that it throws if the given storage name is not one of 'local' or 'session'. */ sqlite3.oo1.JsStorageDb = function(storageName='session'){ if('session'!==storageName && 'local'!==storageName){ toss3("JsStorageDb db name must be one of 'session' or 'local'."); } dbCtorHelper.call(this, { filename: storageName, flags: 'c', vfs: "kvvfs" }); }; const jdb = sqlite3.oo1.JsStorageDb; jdb.prototype = Object.create(DB.prototype); /** Equivalent to sqlite3_js_kvvfs_clear(). */ jdb.clearStorage = capi.sqlite3_js_kvvfs_clear; /** Clears this database instance's storage or throws if this instance has been closed. Returns the number of database blocks which were cleaned up. */ jdb.prototype.clearStorage = function(){ return jdb.clearStorage(affirmDbOpen(this).filename); }; /** Equivalent to sqlite3_js_kvvfs_size(). */ jdb.storageSize = capi.sqlite3_js_kvvfs_size; /** Returns the _approximate_ number of bytes this database takes up in its storage or throws if this instance has been closed. */ jdb.prototype.storageSize = function(){ return jdb.storageSize(affirmDbOpen(this).filename); }; |
︙ | ︙ | |||
11143 11144 11145 11146 11147 11148 11149 | wasmfsOpfsEnabled: true if persistent storage is enabled in the current environment. Only files stored under wasmfsOpfsDir will persist using that mechanism, however. It is legal to use the non-WASMFS OPFS VFS to open a database via a URI-style db filename. | | | 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335 11336 11337 11338 11339 11340 | wasmfsOpfsEnabled: true if persistent storage is enabled in the current environment. Only files stored under wasmfsOpfsDir will persist using that mechanism, however. It is legal to use the non-WASMFS OPFS VFS to open a database via a URI-style db filename. vfsList: result of sqlite3.capi.sqlite3_js_vfs_list() } } ``` ==================================================================== "open" a database |
︙ | ︙ | |||
11201 11202 11203 11204 11205 11206 11207 | Message format: ``` { type: "close", messageId: ...as above... dbId: ...as above... | | | > > | | | 11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 | Message format: ``` { type: "close", messageId: ...as above... dbId: ...as above... args: OPTIONAL {unlink: boolean} } ``` If the `dbId` does not refer to an opened ID, this is a no-op. If the `args` object contains a truthy `unlink` value then the database will be unlinked (deleted) after closing it. The inability to close a db (because it's not opened) or delete its file does not trigger an error. Response: ``` { type: "close", messageId: ...as above..., |
︙ | ︙ | |||
11340 11341 11342 11343 11344 11345 11346 | this.dbs[getDbId(db)] = db; if(!this.defaultDb) this.defaultDb = db; return db; }, close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; | | > | < < < | | 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 | this.dbs[getDbId(db)] = db; if(!this.defaultDb) this.defaultDb = db; return db; }, close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; const filename = db.filename; const pVfs = sqlite3.capi.wasm.sqlite3_wasm_db_vfs(db.pointer, 0); db.close(); if(db===this.defaultDb) this.defaultDb = undefined; if(alsoUnlink && filename && pVfs){ sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(pVfs, filename); } } }, /** Posts the given worker message value. If xferList is provided, it must be an array, in which case a copy of it passed as postMessage()'s second argument and xferList.length is set to |
︙ | ︙ | |||
11410 11411 11412 11413 11414 11415 11416 | if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } const rc = Object.create(null); const pDir = sqlite3.capi.sqlite3_wasmfs_opfs_dir(); if(!args.filename || ':memory:'===args.filename){ oargs.filename = args.filename || ''; | < < | | | 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 | if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } const rc = Object.create(null); const pDir = sqlite3.capi.sqlite3_wasmfs_opfs_dir(); if(!args.filename || ':memory:'===args.filename){ oargs.filename = args.filename || ''; }else{ oargs.filename = args.filename; } const db = wState.open(oargs); rc.filename = db.filename; rc.persistent = (!!pDir && db.filename.startsWith(pDir+'/')) || sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); rc.dbId = getDbId(db); return rc; }, close: function(ev){ const db = getMsgDb(ev,false); const response = { |
︙ | ︙ | |||
11499 11500 11501 11502 11503 11504 11505 | [ 'wasmfsOpfsDir', 'bigIntEnabled' ].forEach(function(k){ if(Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; }); rc.wasmfsOpfsEnabled = !!sqlite3.capi.sqlite3_wasmfs_opfs_dir(); rc.version = sqlite3.version; | | | 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 | [ 'wasmfsOpfsDir', 'bigIntEnabled' ].forEach(function(k){ if(Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; }); rc.wasmfsOpfsEnabled = !!sqlite3.capi.sqlite3_wasmfs_opfs_dir(); rc.version = sqlite3.version; rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); return rc; }, /** TO(RE)DO, once we can abstract away access to the JS environment's virtual filesystem. Currently this always throws. |
︙ | ︙ | |||
11613 11614 11615 11616 11617 11618 11619 | *********************************************************************** This file holds the synchronous half of an sqlite3_vfs implementation which proxies, in a synchronous fashion, the asynchronous Origin-Private FileSystem (OPFS) APIs using a second Worker, implemented in sqlite3-opfs-async-proxy.js. This file is intended to be appended to the main sqlite3 JS deliverable somewhere | | | | > > > | | 11794 11795 11796 11797 11798 11799 11800 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 | *********************************************************************** This file holds the synchronous half of an sqlite3_vfs implementation which proxies, in a synchronous fashion, the asynchronous Origin-Private FileSystem (OPFS) APIs using a second Worker, implemented in sqlite3-opfs-async-proxy.js. This file is intended to be appended to the main sqlite3 JS deliverable somewhere after sqlite3-api-oo1.js and before sqlite3-api-cleanup.js. */ 'use strict'; self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /** installOpfsVfs() returns a Promise which, on success, installs an sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs which accept a VFS. It is intended to be called via sqlite3ApiBootstrap.initializersAsync or an equivalent mechanism. The installed VFS uses the Origin-Private FileSystem API for all file storage. On error it is rejected with an exception explaining the problem. Reasons for rejection include, but are not limited to: - The counterpart Worker (see below) could not be loaded. - The environment does not support OPFS. That includes when |
︙ | ︙ | |||
11646 11647 11648 11649 11650 11651 11652 | - It requires the SharedArrayBuffer and Atomics classes, and the former is only available if the HTTP server emits the so-called COOP and COEP response headers. These features are required for proxying OPFS's synchronous API via the synchronous interface required by the sqlite3_vfs API. | | < < < < | > > > > | | | | | | | | | | | | | > | < < > | 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 | - It requires the SharedArrayBuffer and Atomics classes, and the former is only available if the HTTP server emits the so-called COOP and COEP response headers. These features are required for proxying OPFS's synchronous API via the synchronous interface required by the sqlite3_vfs API. - This function may only be called a single time. When called, this function removes itself from the sqlite3 object. All arguments to this function are for internal/development purposes only. They do not constitute a public API and may change at any time. The argument may optionally be a plain object with the following configuration options: - proxyUri: as described above - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables logging of errors. 2 enables logging of warnings and errors. 3 additionally enables debugging info. - sanityChecks (=false): if true, some basic sanity tests are run on the OPFS VFS API after it's initialized, before the returned Promise resolves. On success, the Promise resolves to the top-most sqlite3 namespace object and that object gets a new object installed in its `opfs` property, containing several OPFS-specific utilities. */ const installOpfsVfs = function callee(options){ if(!self.SharedArrayBuffer || !self.Atomics || !self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle || !self.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator.storage.getDirectory){ return Promise.reject( new Error("This environment does not have OPFS support.") ); } if(!options || 'object'!==typeof options){ options = Object.create(null); } const urlParams = new URL(self.location.href).searchParams; if(undefined===options.verbose){ options.verbose = urlParams.has('opfs-verbose') ? 3 : 2; } if(undefined===options.sanityChecks){ options.sanityChecks = urlParams.has('opfs-sanity-check'); } |
︙ | ︙ | |||
11711 11712 11713 11714 11715 11716 11717 | }; const logImpl = (level,...args)=>{ if(options.verbose>level) loggers[level]("OPFS syncer:",...args); }; const log = (...args)=>logImpl(2, ...args); const warn = (...args)=>logImpl(1, ...args); const error = (...args)=>logImpl(0, ...args); | < | 11895 11896 11897 11898 11899 11900 11901 11902 11903 11904 11905 11906 11907 11908 | }; const logImpl = (level,...args)=>{ if(options.verbose>level) loggers[level]("OPFS syncer:",...args); }; const log = (...args)=>logImpl(2, ...args); const warn = (...args)=>logImpl(1, ...args); const error = (...args)=>logImpl(0, ...args); const toss = function(...args){throw new Error(args.join(' '))}; const capi = sqlite3.capi; const wasm = capi.wasm; const sqlite3_vfs = capi.sqlite3_vfs; const sqlite3_file = capi.sqlite3_file; const sqlite3_io_methods = capi.sqlite3_io_methods; /** |
︙ | ︙ | |||
11756 11757 11758 11759 11760 11761 11762 | r(metrics[k] = Object.create(null)); } let s = metrics.s11n = Object.create(null); s = s.serialize = Object.create(null); s.count = s.time = 0; s = metrics.s11n.deserialize = Object.create(null); s.count = s.time = 0; | < < < | 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 | r(metrics[k] = Object.create(null)); } let s = metrics.s11n = Object.create(null); s = s.serialize = Object.create(null); s.count = s.time = 0; s = metrics.s11n.deserialize = Object.create(null); s.count = s.time = 0; } }/*metrics*/; const promiseReject = function(err){ opfsVfs.dispose(); return promiseReject_(err); }; const W = new Worker(options.proxyUri); |
︙ | ︙ | |||
11821 11822 11823 11824 11825 11826 11827 | Atomics.notify() on that slot. The approach of using a single SAB to serialize comms for all instances might(?) lead to deadlock situations in multi-db cases. We should probably have one SAB here with a single slot for locking a per-file initialization step and then allocate a separate SAB like the above one for each file. That will | | > > > | > | | | | | | | | | > | < | > | 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 12021 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 | Atomics.notify() on that slot. The approach of using a single SAB to serialize comms for all instances might(?) lead to deadlock situations in multi-db cases. We should probably have one SAB here with a single slot for locking a per-file initialization step and then allocate a separate SAB like the above one for each file. That will require a bit of acrobatics but should be feasible. The most problematic part is that xOpen() would have to use postMessage() to communicate its SharedArrayBuffer, and mixing that approach with Atomics.wait/notify() gets a bit messy. */ const state = Object.create(null); state.verbose = options.verbose; state.littleEndian = (()=>{ const buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true /* ==>littleEndian */); // Int16Array uses the platform's endianness. return new Int16Array(buffer)[0] === 256; })(); /** Whether the async counterpart should log exceptions to the serialization channel. That produces a great deal of noise for seemingly innocuous things like xAccess() checks for missing files, so this option may have one of 3 values: 0 = no exception logging 1 = only log exceptions for "significant" ops like xOpen(), xRead(), and xWrite(). 2 = log all exceptions. */ state.asyncS11nExceptions = 1; /* Size of file I/O buffer block. 64k = max sqlite3 page size, and xRead/xWrite() will never deal in blocks larger than that. */ state.fileBufferSize = 1024 * 64; state.sabS11nOffset = state.fileBufferSize; /** The size of the block in our SAB for serializing arguments and result values. Needs to be large enough to hold serialized values of any of the proxied APIs. Filenames are the largest part but are limited to opfsVfs.$mxPathname bytes. */ state.sabS11nSize = opfsVfs.$mxPathname * 2; /** The SAB used for all data I/O between the synchronous and async halves (file i/o and arg/result s11n). */ state.sabIO = new SharedArrayBuffer( state.fileBufferSize/* file i/o block */ + state.sabS11nSize/* argument/result serialization block */ ); state.opIds = Object.create(null); const metrics = Object.create(null); |
︙ | ︙ | |||
11895 11896 11897 11898 11899 11900 11901 | state.opIds.xSync = i++; state.opIds.xTruncate = i++; state.opIds.xUnlock = i++; state.opIds.xWrite = i++; state.opIds.mkdir = i++; state.opIds['opfs-async-metrics'] = i++; state.opIds['opfs-async-shutdown'] = i++; | > > > > | > > | 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 12090 12091 12092 12093 12094 12095 12096 12097 12098 12099 12100 | state.opIds.xSync = i++; state.opIds.xTruncate = i++; state.opIds.xUnlock = i++; state.opIds.xWrite = i++; state.opIds.mkdir = i++; state.opIds['opfs-async-metrics'] = i++; state.opIds['opfs-async-shutdown'] = i++; /* The retry slot is used by the async part for wait-and-retry semantics. Though we could hypothetically use the xSleep slot for that, doing so might lead to undesired side effects. */ state.opIds.retry = i++; state.sabOP = new SharedArrayBuffer( i * 4/* ==sizeof int32, noting that Atomics.wait() and friends can only function on Int32Array views of an SAB. */); opfsUtil.metrics.reset(); } /** SQLITE_xxx constants to export to the async worker counterpart... */ state.sq3Codes = Object.create(null); |
︙ | ︙ | |||
11936 11937 11938 11939 11940 11941 11942 | async op. */ const opRun = (op,...args)=>{ const opNdx = state.opIds[op] || toss("Invalid op ID:",op); state.s11n.serialize(...args); Atomics.store(state.sabOPView, state.opIds.rc, -1); Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); | | > | > > > | | > > | | | > > | 12127 12128 12129 12130 12131 12132 12133 12134 12135 12136 12137 12138 12139 12140 12141 12142 12143 12144 12145 12146 12147 12148 12149 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 | async op. */ const opRun = (op,...args)=>{ const opNdx = state.opIds[op] || toss("Invalid op ID:",op); state.s11n.serialize(...args); Atomics.store(state.sabOPView, state.opIds.rc, -1); Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); Atomics.notify(state.sabOPView, state.opIds.whichOp) /* async thread will take over here */; const t = performance.now(); Atomics.wait(state.sabOPView, state.opIds.rc, -1) /* When this wait() call returns, the async half will have completed the operation and reported its results. */; const rc = Atomics.load(state.sabOPView, state.opIds.rc); metrics[op].wait += performance.now() - t; if(rc && state.asyncS11nExceptions){ const err = state.s11n.deserialize(); if(err) error(op+"() async error:",...err); } return rc; }; const initS11n = ()=>{ /** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ACHTUNG: this code is 100% duplicated in the other half of this proxy! The documentation is maintained in the "synchronous half". !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! This proxy de/serializes cross-thread function arguments and output-pointer values via the state.sabIO SharedArrayBuffer, using the region defined by (state.sabS11nOffset, state.sabS11nOffset]. Only one dataset is recorded at a time. This is not a general-purpose format. It only supports the range of operations, and data sizes, needed by the sqlite3_vfs and sqlite3_io_methods operations. Serialized data are transient and this serialization algorithm may change at any time. The data format can be succinctly summarized as: Nt...Td...D Where: |
︙ | ︙ | |||
11984 11985 11986 11987 11988 11989 11990 | - ...D = raw bytes of the 2nd and subsequent arguments (per-type size). All types except strings have fixed sizes. Strings are stored using their TextEncoder/TextDecoder representations. It would arguably make more sense to store them as Int16Arrays of their JS character values, but how best/fastest to get that | | > | 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 | - ...D = raw bytes of the 2nd and subsequent arguments (per-type size). All types except strings have fixed sizes. Strings are stored using their TextEncoder/TextDecoder representations. It would arguably make more sense to store them as Int16Arrays of their JS character values, but how best/fastest to get that in and out of string form is an open point. Initial experimentation with that approach did not gain us any speed. Historical note: this impl was initially about 1% this size by using using JSON.stringify/parse(), but using fit-to-purpose serialization saves considerable runtime. */ if(state.s11n) return state.s11n; const textDecoder = new TextDecoder(), |
︙ | ︙ | |||
12181 12182 12183 12184 12185 12186 12187 | }; }/*static init*/ const sigN = tgt.memberSignature(name); if(sigN.length<2){ toss("Member",name," is not a function pointer. Signature =",sigN); } const memKey = tgt.memberKey(name); | < | > > > < > | < > > > > > > > | 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421 12422 12423 12424 12425 12426 12427 12428 12429 12430 12431 12432 12433 12434 12435 12436 12437 12438 12439 12440 | }; }/*static init*/ const sigN = tgt.memberSignature(name); if(sigN.length<2){ toss("Member",name," is not a function pointer. Signature =",sigN); } const memKey = tgt.memberKey(name); const fProxy = 0 /** This middle-man proxy is only for use during development, to confirm that we always pass the proper number of arguments. We know that the C-level code will always use the correct argument count. */ ? callee.argcProxy(func, sigN) : func; const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); tgt[memKey] = pFunc; if(!tgt.ondispose) tgt.ondispose = []; if(!tgt.ondispose.__removeFuncList){ tgt.ondispose.push('ondispose.__removeFuncList handler', callee.removeFuncList); tgt.ondispose.__removeFuncList = []; } tgt.ondispose.__removeFuncList.push(memKey, pFunc); return (n,f)=>callee(tgt, n, f); }/*installMethod*/; const opTimer = Object.create(null); opTimer.op = undefined; opTimer.start = undefined; const mTimeStart = (op)=>{ opTimer.start = performance.now(); opTimer.op = op; ++metrics[op].count; }; const mTimeEnd = ()=>( metrics[opTimer.op].time += performance.now() - opTimer.start ); /** Impls for the sqlite3_io_methods methods. Maintenance reminder: members are in alphabetical order to simplify finding them. */ const ioSyncWrappers = { xCheckReservedLock: function(pFile,pOut){ /** As of late 2022, only a single lock can be held on an OPFS file. We have no way of checking whether any _other_ db connection has a lock except by trying to obtain and (on success) release a sync-handle for it, but doing so would involve an inherent race condition. For the time being, pending a better solution, we simply report whether the given pFile instance has a lock. */ const f = __openFiles[pFile]; wasm.setMemValue(pOut, f.lockMode ? 1 : 0, 'i32'); return 0; }, xClose: function(pFile){ mTimeStart('xClose'); let rc = 0; |
︙ | ︙ | |||
12271 12272 12273 12274 12275 12276 12277 | }else{ f.lockType = lockType; } mTimeEnd(); return rc; }, xRead: function(pFile,pDest,n,offset64){ | < | > > > > | | 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 | }else{ f.lockType = lockType; } mTimeEnd(); return rc; }, xRead: function(pFile,pDest,n,offset64){ mTimeStart('xRead'); const f = __openFiles[pFile]; let rc; try { rc = opRun('xRead',pFile, n, Number(offset64)); if(0===rc || capi.SQLITE_IOERR_SHORT_READ===rc){ /** Results get written to the SharedArrayBuffer f.sabView. Because the heap is _not_ a SharedArrayBuffer, we have to copy the results. TypedArray.set() seems to be the fastest way to copy this. */ wasm.heap8u().set(f.sabView.subarray(0, n), pDest); } }catch(e){ error("xRead(",arguments,") failed:",e,f); rc = capi.SQLITE_IOERR_READ; } mTimeEnd(); |
︙ | ︙ | |||
12311 12312 12313 12314 12315 12316 12317 | rc = opRun('xUnlock', pFile, lockType); } if( 0===rc ) f.lockType = lockType; mTimeEnd(); return rc; }, xWrite: function(pFile,pSrc,n,offset64){ | < | 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 | rc = opRun('xUnlock', pFile, lockType); } if( 0===rc ) f.lockType = lockType; mTimeEnd(); return rc; }, xWrite: function(pFile,pSrc,n,offset64){ mTimeStart('xWrite'); const f = __openFiles[pFile]; let rc; try { f.sabView.set(wasm.heap8u().subarray(pSrc, pSrc+n)); rc = opRun('xWrite', pFile, n, Number(offset64)); }catch(e){ |
︙ | ︙ | |||
12377 12378 12379 12380 12381 12382 12383 | to encode them... TextEncoder can do that for us. */ warn("OPFS xGetLastError() has nothing sensible to return."); return 0; }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); | < < < < < < < < < < < < < < < < < < < < < < | 12587 12588 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 12599 12600 | to encode them... TextEncoder can do that for us. */ warn("OPFS xGetLastError() has nothing sensible to return."); return 0; }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); if(0===zName){ zName = randomFilename(); }else if('number'===typeof zName){ zName = wasm.cstringToJs(zName); } const fh = Object.create(null); fh.fid = pFile; |
︙ | ︙ | |||
12682 12683 12684 12685 12686 12687 12688 | } break; } default: promiseReject(e); error("Unexpected message from the async worker:",data); break; | | < | < < | 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 | } break; } default: promiseReject(e); error("Unexpected message from the async worker:",data); break; }/*switch(data.type)*/ }/*W.onmessage()*/; })/*thePromise*/; return thePromise; }/*installOpfsVfs()*/; installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; self.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ if(sqlite3.scriptInfo && !sqlite3.scriptInfo.isWorker){ return; } try{ let proxyJs = installOpfsVfs.defaultProxyUri; if(sqlite3.scriptInfo.sqlite3Dir){ |
︙ | ︙ | |||
12805 12806 12807 12808 12809 12810 12811 | if (typeof exports === 'object' && typeof module === 'object') module.exports = sqlite3InitModule; else if (typeof define === 'function' && define['amd']) define([], function() { return sqlite3InitModule; }); else if (typeof exports === 'object') exports["sqlite3InitModule"] = sqlite3InitModule; /* extern-post-js.js must be appended to the resulting sqlite3.js | | | > > > | 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 | if (typeof exports === 'object' && typeof module === 'object') module.exports = sqlite3InitModule; else if (typeof define === 'function' && define['amd']) define([], function() { return sqlite3InitModule; }); else if (typeof exports === 'object') exports["sqlite3InitModule"] = sqlite3InitModule; /* extern-post-js.js must be appended to the resulting sqlite3.js file. It gets its name from being used as the value for the --extern-post-js=... Emscripten flag. Note that this code, unlike most of the associated JS code, runs outside of the Emscripten-generated module init scope, in the current global scope. */ (function(){ /** In order to hide the sqlite3InitModule()'s resulting Emscripten module from downstream clients (and simplify our documentation by being able to elide those details), we rewrite sqlite3InitModule() to return the sqlite3 object. |
︙ | ︙ | |||
12829 12830 12831 12832 12833 12834 12835 | /** We need to add some state which our custom Module.locateFile() can see, but an Emscripten limitation currently prevents us from attaching it to the sqlite3InitModule function object: https://github.com/emscripten-core/emscripten/issues/18071 | | | | > > > > > | 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 | /** We need to add some state which our custom Module.locateFile() can see, but an Emscripten limitation currently prevents us from attaching it to the sqlite3InitModule function object: https://github.com/emscripten-core/emscripten/issues/18071 The only(?) current workaround is to temporarily stash this state into the global scope and delete it when sqlite3InitModule() is called. */ const initModuleState = self.sqlite3InitModuleState = Object.assign(Object.create(null),{ moduleScript: self?.document?.currentScript, isWorker: ('undefined' !== typeof WorkerGlobalScope), location: self.location, urlParams: new URL(self.location.href).searchParams }); if(initModuleState.urlParams.has('sqlite3.dir')){ initModuleState.sqlite3Dir = initModuleState.urlParams.get('sqlite3.dir') +'/'; }else if(initModuleState.moduleScript){ const li = initModuleState.moduleScript.src.split('/'); li.pop(); initModuleState.sqlite3Dir = li.join('/') + '/'; } //console.warn("initModuleState =",initModuleState); self.sqlite3InitModule = (...args)=>{ //console.warn("Using replaced sqlite3InitModule()",self.location); return originalInit(...args).then((EmscriptenModule)=>{ if(self.window!==self && (EmscriptenModule['ENVIRONMENT_IS_PTHREAD'] || EmscriptenModule['_pthread_self'] |
︙ | ︙ | |||
12885 12886 12887 12888 12889 12890 12891 12892 | console.warn("Replaced sqlite3InitModule()"); console.warn("self.location.href =",self.location.href); if('undefined' !== typeof document){ console.warn("document.currentScript.src =", document?.currentScript?.src); } } })(); | > > > > > > > > | 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 | console.warn("Replaced sqlite3InitModule()"); console.warn("self.location.href =",self.location.href); if('undefined' !== typeof document){ console.warn("document.currentScript.src =", document?.currentScript?.src); } } /* Replace the various module exports performed by the Emscripten glue... */ if (typeof exports === 'object' && typeof module === 'object') module.exports = sqlite3InitModule; else if (typeof exports === 'object') exports["sqlite3InitModule"] = sqlite3InitModule; /* AMD modules get injected in a way we cannot override, so we can't handle those here. */ })(); |
Changes to jswasm/sqlite3.wasm.
cannot compute difference between binary files