aboutsummaryrefslogtreecommitdiff
path: root/ext/fiddle/sqlite3-api.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/fiddle/sqlite3-api.js')
-rw-r--r--ext/fiddle/sqlite3-api.js165
1 files changed, 165 insertions, 0 deletions
diff --git a/ext/fiddle/sqlite3-api.js b/ext/fiddle/sqlite3-api.js
new file mode 100644
index 000000000..65e6b00f6
--- /dev/null
+++ b/ext/fiddle/sqlite3-api.js
@@ -0,0 +1,165 @@
+/*
+ 2022-05-22
+
+ The author disclaims copyright to this source code. In place of a
+ legal notice, here is a blessing:
+
+ * May you do good and not evil.
+ * May you find forgiveness for yourself and forgive others.
+ * May you share freely, never taking more than you give.
+
+ ***********************************************************************
+
+ This file is intended to be loaded after loading
+ sqlite3-module.wasm. It sets one of any number of potential
+ bindings using that API, this one as closely matching the C-native
+ API as is feasible.
+
+ Note that this file is not named sqlite3.js because that file gets
+ generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
+
+ The API gets installed as self.sqlite3, where self is expected to be
+ either the global window or Worker object.
+
+ Because using this API properly requires some degree of WASM-related
+ magic, it is not recommended that this API be used as-is in
+ client-level code, but instead is intended to be used as a basis for
+ APIs more appropriate for high-level client code.
+
+ This file installs namespace.sqlite3, where namespace is `self`,
+ meaning either the global window or worker, depending on where this
+ is loaded from.
+*/
+(function(namespace){
+ /* For reference: sql.js does essentially everything we want and
+ it solves much of the wasm-related voodoo, but we'll need a
+ different structure because we want the db connection to run in
+ a worker thread and feed data back into the main
+ thread. Regardless of those differences, it makes a great point
+ of reference:
+
+ https://github.com/sql-js/sql.js
+
+ Some of the specific design goals here:
+
+ - Bind a low-level sqlite3 API which is close to the native one
+ in terms of usage.
+
+ - Create a higher-level one, more akin to sql.js and
+ node.js-style implementations. This one would speak directly
+ to the low-level API. This API could be used by clients who
+ import the low-level API directly into their main thread
+ (which we don't want to recommend but also don't want to
+ outright forbid).
+
+ - Create a second higher-level one which speaks to the
+ low-level API via worker messages. This one would be intended
+ for use in the main thread, talking to the low-level UI via
+ worker messages. Because workers have only a single message
+ channel, some acrobatics will be needed here to feed async
+ work results back into client-side callbacks (as those
+ callbacks cannot simply be passed to the worker). Exactly
+ what those acrobatics should look like is not yet entirely
+ clear and much experimentation is pending.
+ */
+
+ /**
+ Set up the main sqlite3 binding API here, mimicking the C API as
+ closely as we can.
+
+ Attribution: though not a direct copy/paste, much of what
+ follows is strongly influenced by the sql.js implementation.
+ */
+ const api = {
+ /* It is important that the following integer values match
+ those from the C code. Ideally we could fetch them from the
+ C API, e.g., in the form of a JSON object, but getting that
+ JSON string constructed within our current confised is
+ currently not worth the effort. */
+ /* Minimum subset of sqlite result codes we'll need. */
+ SQLITE_OK: 0,
+ SQLITE_ROW: 100,
+ SQLITE_DONE: 101,
+ /* sqlite data types */
+ SQLITE_INTEGER: 1,
+ SQLITE_FLOAT: 2,
+ SQLITE_TEXT: 3,
+ SQLITE_BLOB: 4,
+ /* sqlite encodings, used for creating UDFs, noting that we
+ will only support UTF8. */
+ SQLITE_UTF8: 1
+ };
+ const cwrap = Module.cwrap;
+ [/* C-side functions to bind. Each entry is an array with 3 or 4
+ elements:
+
+ ["c-side name",
+ "result type" (cwrap() syntax),
+ [arg types in cwrap() syntax]
+ ]
+
+ If it has 4 elements, the first one is an alternate name to
+ use for the JS-side binding. That's required when overloading
+ a binding for two different uses.
+ */
+ ["sqlite3_open", "number", ["string", "number"]],
+ ["sqlite3_close_v2", "number", ["number"]],
+ ["sqlite3_exec", "number",
+ ["number", "string", "number", "number", "number"]],
+ ["sqlite3_changes", "number", ["number"]],
+ ["sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"]],
+ ["sqlite3_prepare_v2_sqlptr",
+ /* Impl which requires that the 2nd argument be a pointer to
+ the SQL, instead of a string. This is used for cases where
+ we require a non-NULL value for the final argument. We may
+ or may not need this, depending on how our higher-level
+ API shapes up, but this code's spiritual guide (sql.js)
+ uses it we we'll include it. */
+ "sqlite3_prepare_v2",
+ "number", ["number", "number", "number", "number", "number"]],
+ ["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]],
+ ["sqlite3_bind_blob","number",["number", "number", "number", "number", "number"]],
+ ["sqlite3_bind_double","number",["number", "number", "number"]],
+ ["sqlite3_bind_int","number",["number", "number", "number"]],
+ ["sqlite3_bind_parameter_index","number",["number", "string"]],
+ ["sqlite3_step", "number", ["number"]],
+ ["sqlite3_errmsg", "string", ["number"]],
+ ["sqlite3_column_count","number",["number"]],
+ ["sqlite3_data_count", "number", ["number"]],
+ ["sqlite3_column_count", "number", ["number"]],
+ ["sqlite3_column_double","number",["number", "number"]],
+ ["sqlite3_column_text","string",["number", "number"]],
+ ["sqlite3_column_blob","number", ["number", "number"]],
+ ["sqlite3_column_bytes","number",["number", "number"]],
+ ["sqlite3_column_type","number",["number", "number"]],
+ ["sqlite3_column_name","string",["number", "number"]],
+ ["sqlite3_reset", "number", ["number"]],
+ ["sqlite3_clear_bindings","number",["number"]],
+ ["sqlite3_finalize", "number", ["number"]],
+ ["sqlite3_create_function_v2", "number",
+ ["number", "string", "number", "number",
+ "number", "number", "number", "number",
+ "number"]],
+ ["sqlite3_value_type", "number", ["number"]],
+ ["sqlite3_value_bytes","number",["number"]],
+ ["sqlite3_value_text", "string", ["number"]],
+ ["sqlite3_value_blob", "number", ["number"]],
+ ["sqlite3_value_double","number",["number"]],
+ ["sqlite3_result_double",null,["number", "number"]],
+ ["sqlite3_result_null",null,["number"]],
+ ["sqlite3_result_text",null,["number", "string", "number", "number"]],
+ ["sqlite3_result_blob",null,["number", "number", "number", "number"]],
+ ["sqlite3_result_int",null,["number", "number"]],
+ ["sqlite3_result_error",null,["number", "string", "number"]],
+ ["sqlite3_libversion", "string", []],
+ ["sqlite3_sourceid", "string", []]
+ //["sqlite3_sql", "string", ["number"]],
+ //["sqlite3_normalized_sql", "string", ["number"]]
+ ].forEach(function(e){
+ const a = Array.prototype.slice.call(e);
+ const k = (4==a.length) ? a.shift() : a[0];
+ api[k] = cwrap.apply(this, a);
+ });
+ //console.debug("libversion =",api.sqlite3_libversion());
+ namespace.sqlite3 = api;
+})(self/*worker or window*/);