summaryrefslogtreecommitdiff
path: root/src/napi-sqlite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/napi-sqlite.c')
-rw-r--r--src/napi-sqlite.c932
1 files changed, 0 insertions, 932 deletions
diff --git a/src/napi-sqlite.c b/src/napi-sqlite.c
deleted file mode 100644
index d4352a2..0000000
--- a/src/napi-sqlite.c
+++ /dev/null
@@ -1,932 +0,0 @@
-#include "i18n.h"
-#include "logerr.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <node/node_api.h>
-#include <sqlite3.h>
-
-
-static napi_value ffi_open(napi_env env, napi_callback_info info);
-static napi_value ffi_exec(napi_env env, napi_callback_info info);
-static napi_value ffi_all (napi_env env, napi_callback_info info);
-static napi_value ffi_run (napi_env env, napi_callback_info info);
-
-
-struct NAPIContext {
- napi_env env;
- napi_value value;
- uint32_t index;
-};
-
-struct Fn {
- const char *const label;
- napi_value(*const handle)(napi_env env, napi_callback_info info);
-};
-
-static const struct Fn fns[] = {
- { .label = "open", .handle = ffi_open, },
- { NULL, NULL },
-};
-
-struct Fn methods[] = {
- { .label = "exec", .handle = ffi_exec, },
- { .label = "all", .handle = ffi_all, },
- { .label = "run", .handle = ffi_run, },
- { NULL, NULL },
-};
-
-static const napi_type_tag SQLITE_DB_TYPE_TAG = {
- 0x0e9614d459f746cc, 0x88b814a5dc5c4cf7
-};
-
-static const int
-SQLITE_OPEN_FLAGS =
- /*
- From https://www.sqlite.org/c3ref/open.html:
-
- > The database is opened for reading and writing, and is created if
- > it does not already exist. This is the behavior that is always
- > used for sqlite3_open() and sqlite3_open16().
- */
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
-
- /*
- From https://www.sqlite.org/c3ref/open.html:
-
- > The new database connection will use the "serialized" threading
- > mode. This means the multiple threads can safely attempt to use
- > the same database connection at the same time. (Mutexes will block
- > any actual concurrency, but in this mode there is no harm in
- > trying.)
- */
- SQLITE_OPEN_FULLMUTEX |
-
- /*
- From https://www.sqlite.org/c3ref/open.html:
-
- > The database connection comes up in "extended result code mode".
- > In other words, the database behaves has if
- > sqlite3_extended_result_codes(db,1) where called on the database
- > connection as soon as the connection is created. In addition to
- > setting the extended result code mode, this flag also causes
- > sqlite3_open_v2() to return an extended result code.
-
- From https://www.sqlite.org/c3ref/extended_result_codes.html:
-
- > The sqlite3_extended_result_codes() routine enables or disables the
- > extended result codes feature of SQLite. The extended result codes
- > are disabled by default for historical compatibility.
- */
- SQLITE_OPEN_EXRESCODE;
-
-
-static const char *const
-FIX_SQLITE_PRAGMAS =
- /*
- From https://research.cs.wisc.edu/adsl/Publications/alice-osdi14.pdf:
-
- > Similarly, SQLite does not provide durability under the default
- > journal-mode (we became aware of this only after interacting with
- > developers), but its documentation seems misleading.
- >
- > (...)
- >
- > The developers suggest the SQLite vulnerability is actually not a
- > behavior guaranteed by SQLite (specifically, that durability cannot
- > be achieved under rollback journaling); we believe the
- > documentation is misleading.
- >
- > (...)
- >
- > Unclear documentation of application guarantees contributes to the
- > confusion about crash vulnerabilities. During discussions with
- > developers about durability vulnerabilities, we found that SQLite,
- > which proclaims itself as fully ACID-complaint, does not provide
- > durability (even optionally) with the default storage engine,
- > though the documentation suggests it does.
- */
- "PRAGMA journal_mode = WAL;\n"
- "PRAGMA synchronous = EXTRA;\n"
-
- /*
- From https://www.sqlite.org/foreignkeys.html:
-
- > Foreign key constraints are disabled by default (for backwards
- > compatibility), so must be enabled separately for each database
- > connection.
- */
- "PRAGMA foreign_keys = ON;\n"
- ;
-
-
-// FIXME: make this async
-static napi_value
-ffi_exec(napi_env env, napi_callback_info info) {
- size_t argc = 1;
- napi_value argv[1];
- int sqlite_rc;
- napi_status napi_rc;
- char *sql = NULL;
- size_t sql_size1;
- size_t sql_size2;
- sqlite3 *db = NULL;
- char *error_msg = NULL;
-
- napi_rc = napi_get_cb_info(
- env,
- info,
- &argc,
- argv,
- NULL,
- (void *)&db
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "111TODO ERRCODE",
- "Failed to parse arguments TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(env, argv[0], NULL, 0, &sql_size1);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "222TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
-
- if (sql_size1 == SIZE_MAX) {
- napi_throw_error(
- env,
- "SQLITE_EOVERFLOW",
- "TODO "
- );
- goto out;
- }
- sql_size1++; // include the NULL-terminator in size measurement
-
- sql = malloc(sql_size1);
- if (!sql) {
- napi_throw_error(
- env,
- "SQLITE_ENOMEM",
- "TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(
- env,
- argv[0],
- sql,
- sql_size1,
- &sql_size2
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
- assert(sql_size1 == sql_size2 + 1 &&
- "Unstable behaviour from Node-API");
- assert(sql);
-
- sqlite_rc = sqlite3_exec(
- db,
- sql,
- NULL,
- NULL,
- &error_msg
- );
- if (sqlite_rc != SQLITE_OK) {
- napi_throw_error(
- env,
- "3iii33TODO ERRCODE",
- sqlite3_errstr(sqlite_rc)
- );
- goto out;
- }
-
-out:
- if (error_msg) {
- sqlite3_free(error_msg);
- }
- if (sql) {
- free(sql);
- }
- return NULL;
-}
-
-
-static int
-accumulate_all_results(
- void *ctxptr,
- int argc,
- char **argv,
- char **column_names
-) {
- int rc = 0;
-
- napi_status napi_rc;
- napi_value row = NULL;
-
- struct NAPIContext *ctx = ctxptr;
- napi_env env = ctx->env;
- napi_value results = ctx->value;
- uint32_t index = ctx->index;
-
- napi_rc = napi_create_object(env, &row);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "TODO i18n"
- );
- rc = -1;
- goto out;
- }
-
- for (int i = 0; i < argc; i++) {
- const char *const column_name = column_names[i];
- const char *const column_value = argv[i];
-
- napi_value str; // FIXME: dispatch on the type
- napi_rc = napi_create_string_utf8(
- env,
- column_value,
- NAPI_AUTO_LENGTH,
- &str
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "SQLITE_MiiiETHOD_CREATE",
- "TODO i18n"
- );
- rc = -1;
- goto out;
- }
-
- napi_rc = napi_set_named_property(
- env,
- row,
- column_name,
- str
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "iSQLITE_MiETHOD_CREATE",
- "TODO i18n"
- );
- rc = -1;
- goto out;
- }
- }
-
- napi_rc = napi_set_element(
- env,
- results,
- index,
- row
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "TODO i18n"
- );
- rc = -1;
- goto out;
- }
-
- ctx->index++;
-
-out:
- return rc;
-}
-
-static napi_value
-ffi_all(napi_env env, napi_callback_info info) {
- napi_value ret = NULL;
-
- size_t argc = 1;
- napi_value argv[1];
- int sqlite_rc;
- napi_status napi_rc;
- char *sql = NULL;
- size_t sql_size1;
- size_t sql_size2;
- sqlite3 *db = NULL;
- char *error_msg = NULL;
- napi_value results_array = NULL;
-
- napi_rc = napi_get_cb_info(
- env,
- info,
- &argc,
- argv,
- NULL,
- (void *)&db
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "111TODO ERRCODE",
- "Failed to parse arguments TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(env, argv[0], NULL, 0, &sql_size1);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "222TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
-
- if (sql_size1 == SIZE_MAX) {
- napi_throw_error(
- env,
- "SQLITE_EOVERFLOW",
- "TODO "
- );
- goto out;
- }
- sql_size1++; // include the NULL-terminator in size measurement
-
- sql = malloc(sql_size1);
- if (!sql) {
- napi_throw_error(
- env,
- "SQLITE_ENOMEM",
- "TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(
- env,
- argv[0],
- sql,
- sql_size1,
- &sql_size2
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
- assert(sql_size1 == sql_size2 + 1 &&
- "Unstable behaviour from Node-API");
- assert(sql);
-
- napi_rc = napi_create_array(env, &results_array);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
-
- struct NAPIContext ctx = {
- .env = env,
- .value = results_array,
- .index = 0,
- };
- sqlite_rc = sqlite3_exec(
- db,
- sql,
- accumulate_all_results,
- &ctx,
- &error_msg
- );
- if (sqlite_rc != SQLITE_OK) {
- napi_throw_error(
- env,
- "i3iiii33TODO ERRCODE",
- sqlite3_errstr(sqlite_rc)
- );
- goto out;
- }
-
- ret = results_array;
-
-out:
- if (error_msg) {
- sqlite3_free(error_msg);
- }
- if (sql) {
- free(sql);
- }
- return ret;
-}
-
-static napi_value
-ffi_run(napi_env env, napi_callback_info info) {
- napi_value ret = NULL;
-
- size_t argc = 1;
- napi_value argv[1];
- int sqlite_rc;
- napi_status napi_rc;
- char *sql = NULL;
- size_t sql_size1;
- size_t sql_size2;
- sqlite3 *db = NULL;
- char *error_msg = NULL;
- napi_value results_array = NULL;
-
- napi_rc = napi_get_cb_info(
- env,
- info,
- &argc,
- argv,
- NULL,
- (void *)&db
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "111TODO ERRCODE",
- "Failed to parse arguments TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(env, argv[0], NULL, 0, &sql_size1);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "222TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
-
- if (sql_size1 == SIZE_MAX) {
- napi_throw_error(
- env,
- "SQLITE_EOVERFLOW",
- "TODO "
- );
- goto out;
- }
- sql_size1++; // include the NULL-terminator in size measurement
-
- sql = malloc(sql_size1);
- if (!sql) {
- napi_throw_error(
- env,
- "SQLITE_ENOMEM",
- "TODO i18n"
- );
- goto out;
- }
-
- napi_rc = napi_get_value_string_utf8(
- env,
- argv[0],
- sql,
- sql_size1,
- &sql_size2
- );
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
- assert(sql_size1 == sql_size2 + 1 &&
- "Unstable behaviour from Node-API");
- assert(sql);
-
- napi_rc = napi_create_array(env, &results_array);
- if (napi_rc != napi_ok) {
- napi_throw_error(
- env,
- "TODO ERRCODE",
- "Invalid number was passed as argument TODO i18n"
- );
- goto out;
- }
-
- struct NAPIContext ctx = {
- .env = env,
- .value = results_array,
- .index = 0,
- };
- sqlite_rc = sqlite3_exec(
- db,
- sql,
- accumulate_all_results,
- &ctx,
- &error_msg
- );
- if (sqlite_rc != SQLITE_OK) {
- napi_throw_error(
- env,
- "i3iiii33TODO ERRCODE",
- sqlite3_errstr(sqlite_rc)
- );
- goto out;
- }
-
- ret = results_array;
-
-out:
- if (error_msg) {
- sqlite3_free(error_msg);
- }
- if (sql) {
- free(sql);
- }
- return ret;
-}
-
-
-static void
-sqlite_error_log_callback(void *_data, int rc, const char *const msg) {
- (void)_data;
- logerr("%s\n%s\n", sqlite3_errstr(rc), msg);
-}
-
-static void
-throw_sqlite_error(napi_env env, sqlite3 *const db) {
- const char *const errstr = sqlite3_errstr(sqlite3_errcode(db));
- const char *const errmsg = sqlite3_errmsg(db);
- logerr("%s\n%s\n", errstr, errmsg);
- if (napi_throw_error(env, errstr, errmsg)) {
- logerr("napi_throw_err(%s, \"%s\", \"%s\");\n",
- "env",
- errstr,
- errmsg);
- goto out;
- }
-
-out:
- return;
-}
-
-static void
-throw_node_str_error(
- napi_env env,
- const char *const errstr,
- const char *const errmsg
-) {
- bool is_pending;
-
- if (napi_is_exception_pending(env, &is_pending)) {
- logerr("napi_is_exception_pending(%s, %s);\n",
- "env",
- "&is_pending");
- goto out;
- }
-
- if (is_pending) {
- logerr("exception already pending, not rethrowing");
- goto out;
- }
-
- logerr("%s\n%s\n", errstr, errmsg);
- if (napi_throw_error(env, errstr, errmsg)) {
- logerr("napi_throw_error(%s, \"%s\", \"%s\");\n"
- "env",
- errstr,
- errmsg);
- goto out;
- }
-
-out:
- return;
-}
-
-static void
-throw_node_env_error(napi_env env) {
- const napi_extended_error_info *error_info = NULL;
-
- if (napi_get_last_error_info(env, &error_info)) {
- logerr("napi_get_last_error_info(%s, %s);\n",
- "env",
- "&error_info");
- goto out;
- }
-
- const char *const errstr = "SQLITE_NAPI_ERR";
- const char *const errmsg =
- error_info->error_message ?
- error_info->error_message :
- _(MSG_ERR_NAPI_MISSING_ERRSTR);
- throw_node_str_error(env, errstr, errmsg);
-
-out:
- return;
-}
-
-static void
-finalize_db(napi_env env, void *finalize_data, void *_finalize_hint) {
- (void)_finalize_hint;
- sqlite3 *db = finalize_data;
-
- if (sqlite3_close(db)) {
- logerr("sqlite3_close(%s);\n",
- "db");
- throw_sqlite_error(env, db);
- goto out;
- }
-
-out:
- return;
-}
-
-static napi_value
-ffi_open(napi_env env, napi_callback_info info) {
- napi_value ret = NULL;
-
- size_t argc = 1;
- napi_value argv[1];
- char *filename = NULL;
- size_t filename_size1;
- size_t filename_size2;
- sqlite3 *db = NULL;
- napi_value wrapped_db = NULL;
- char *error_msg = NULL;
-
- if (napi_get_cb_info(env, info, &argc, argv, NULL, NULL)) {
- logerr("napi_get_cb_info(%s, %s, %s, %s, %s, %s);\n",
- "env",
- "info",
- "&argc",
- "argv",
- "NULL",
- "NULL");
- throw_node_env_error(env);
- goto out;
- }
-
- if (napi_get_value_string_utf8(
- env,
- argv[0],
- NULL, // first we query the size of the string
- 0,
- &filename_size1
- )) {
- logerr("napi_get_value_string_utf8(%s, %s, %s, %s, %s);\n",
- "env",
- "argv[0]",
- "NULL",
- "0",
- "&filename_size1");
- throw_node_env_error(env);
- goto out;
- }
-
- if (filename_size1 == SIZE_MAX) {
- logerr("%s: filename_size1 == SIZE_MAX;\n", strerror(EOVERFLOW));
- throw_node_str_error(env, "EOVERFLOW", strerror(EOVERFLOW));
- goto out;
- }
- filename_size1++; // include the NULL-terminator in size measurement
-
- filename = malloc(filename_size1);
- if (!filename) {
- logerr("malloc(filename_size1): %s\n", strerror(errno));
- throw_node_str_error(env, "ENOMEM", strerror(ENOMEM));
- goto out;
- }
-
- if (napi_get_value_string_utf8(
- env,
- argv[0],
- filename,
- filename_size1,
- &filename_size2
- )) {
- logerr("napi_get_value_string_utf8(%s, %s, %s, %s, %s);\n",
- "env",
- "argv[0]",
- "filename",
- "filename_size1",
- "&filename_size2");
- throw_node_env_error(env);
- goto out;
- }
- assert(filename_size1 == filename_size2 + 1 &&
- "Unstable behaviour from Node-API");
- assert(filename && "filename should not be NULL at this point");
-
- if (sqlite3_open_v2(
- filename,
- &db,
- SQLITE_OPEN_FLAGS,
- NULL
- )) {
- logerr("sqlite3_open_v2(%s, %s, %s, %s);\n",
- "filename",
- "&db",
- "SQLITE_OPEN_FLAGS",
- "NULL");
- throw_node_env_error(env);
- goto out;
- }
-
- if (sqlite3_config(
- SQLITE_CONFIG_LOG,
- sqlite_error_log_callback,
- NULL
- )) {
- logerr("sqlite3_config(%s, %s, %s);\n",
- "SQLITE_CONFIG_LOG",
- "sqlite_error_log_callback",
- "NULL");
- throw_sqlite_error(env, db);
- goto out;
- }
-
- if (sqlite3_exec(
- db,
- FIX_SQLITE_PRAGMAS,
- NULL,
- NULL,
- &error_msg
- )) {
- logerr("sqlite3_exec(%s, %s, %s, %s, %s);\n",
- "db",
- "FIX_SQLITE_PRAGMAS",
- "NULL",
- "NULL",
- "&error_msg");
- throw_sqlite_error(env, db);
- goto out;
- }
-
- if (napi_create_object(env, &wrapped_db)) {
- logerr("napi_create_object(%s, %s);\n",
- "env",
- "&wrapped_db");
- throw_node_env_error(env);
- goto out;
- }
-
- if (napi_type_tag_object(
- env,
- wrapped_db,
- &SQLITE_DB_TYPE_TAG
- )) {
- logerr("napi_type_tag_object(%s, %s, %s);\n",
- "env",
- "wrapped_db",
- "&SQLITE_DB_TYPE_TAG");
- throw_node_env_error(env);
- goto out;
- }
-
- if (napi_wrap(
- env,
- wrapped_db,
- db,
- finalize_db,
- NULL,
- NULL
- )) {
- logerr("napi_wrap(%s, %s, %s, %s, %s, %s);\n",
- "env",
- "wrapped_db",
- "db",
- "finalize_db",
- "NULL",
- "NULL");
- throw_node_env_error(env);
- goto out;
- }
-
- for (size_t i = 0; methods[i].label && methods[i].handle; i++) {
- napi_value fn;
- if (napi_create_function(
- env,
- methods[i].label,
- NAPI_AUTO_LENGTH,
- methods[i].handle,
- db,
- &fn
- )) {
- logerr("napi_create_function(%s, %s, %s, %s, %s, %s);\n",
- "env",
- "methods[i].label",
- "NAPI_AUTO_LENGTH",
- "methods[i].handle",
- "db",
- "&fn");
- throw_node_env_error(env);
- goto out;
- }
-
- if (napi_set_named_property(
- env,
- wrapped_db,
- methods[i].label,
- fn
- )) {
- logerr("napi_set_named_property(%s, %s, %s, %s);\n",
- "env,"
- "wrapped_db",
- "methods[i].label",
- "fn");
- throw_node_env_error(env);
- goto out;
- }
- }
- ret = wrapped_db;
-
-out:
- if (error_msg) {
- sqlite3_free(error_msg);
- }
- if (filename) {
- free(filename);
- }
- if (!ret) {
- if (db) {
- if (sqlite3_close(db)) {
- logerr("sqlite3_close(%s);\n",
- "db");
- }
- }
- }
- return ret;
-}
-
-
-static napi_value
-ffi_init(napi_env env, napi_value exports) {
- napi_value ret = exports;
-
- napi_status status;
-
- for (size_t i = 0; fns[i].label && fns[i].handle; i++) {
- napi_value fn;
- status = napi_create_function(
- env,
- fns[i].label,
- NAPI_AUTO_LENGTH,
- fns[i].handle,
- NULL,
- &fn
- );
- if (status != napi_ok) {
- ret = NULL;
- napi_throw_error(
- env,
- "SQLITE_FN_CREATE",
- "Unable to wrap native function TODO i18n"
- );
- goto out;
- }
-
- status = napi_set_named_property(
- env,
- exports,
- fns[i].label,
- fn
- );
- if (status != napi_ok) {
- ret = NULL;
- napi_throw_error(
- env,
- "SQLITE_FN_SETNAME",
- "Unable to populate exports TODO i18n"
- );
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-
-NAPI_MODULE_INIT() {
- return ffi_init(env, exports);
-}