[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/sqlite3 ce41ac18bc 02/62: first commit
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/sqlite3 ce41ac18bc 02/62: first commit |
Date: |
Tue, 14 Mar 2023 11:01:44 -0400 (EDT) |
branch: elpa/sqlite3
commit ce41ac18bc39674a5ff564905f1af166c0cc41b6
Author: Y. N. Lo <gordonynlo@yahoo.com>
Commit: Y. N. Lo <gordonynlo@yahoo.com>
first commit
---
Makefile | 17 +
README.md | 227 ++++++++++++
emacs-module.h | 204 +++++++++++
regression.el | 150 ++++++++
sqlite3-napi-module.c | 975 ++++++++++++++++++++++++++++++++++++++++++++++++++
sqlite3-napi.el | 368 +++++++++++++++++++
tools/gen-consts.py | 26 ++
tools/run.sh | 5 +
8 files changed, 1972 insertions(+)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..830c5dd101
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+CC = gcc
+CFLAGS = -g3 -Wall
+EMACS=$(HOME)/test-emacs/bin/emacs
+
+all: sqlite3-napi-module.so
+
+clean:
+ rm -f *.so
+
+%.so: %.o
+ $(CC) -shared -o $@ $< -lsqlite3
+
+%.o: %.c
+ $(CC) $(CFLAGS) -fPIC -c $<
+
+test:
+ $(EMACS) -batch -Q -l regression.el
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..61458c10be
--- /dev/null
+++ b/README.md
@@ -0,0 +1,227 @@
+# SQLite3 Native API for Emacs 25+
+sqlite-napi is a dynamic module for GNU Emacs that provides
+direct access to the SQLite3 C API.
+## Usage
+~~~el
+(require 'sqlite3-napi)
+
+(let ((dbh)
+ (stmt))
+ (unwind-protect
+ (progn
+ (setq dbh (sqlite3-open "/path/to/db"
+ (logior sqlite-open-readwrite
+ sqlite-open-create)))
+ (setq stmt (sqlite3-prepare dbh "select * from some_table"))
+ (while (= sqlite-row (sqlite3-step stmt))
+ (cl-loop for i from 0 to (1- (sqlite3-column-count stmt)) do
+ (message "Column #%d: %s => %s" i
+ (sqlite3-column-name stmt i)
+ (sqlite3-column-text stmt i)))))
+ ;; Clean up
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh)))
+~~~
+This is alpha software and might crash your Emacs. Save your work before
+trying it out.
+
+## Requirements
+- Emacs 25.1 or above, compiled with module support (`--with-modules`)
+- sqlite3 library and header file
+- C compiler
+## Installation
+~~~sh
+$ git co https://github.com/pekingduck/sqlite3-napi
+$ cd sqlite3-napi
+$ make
+$ cp sqlite3-napi.el sqlite3-napi-module.so /your/elisp/load-path/
+~~~
+Once the code stabilizes I will make it available on melpa.
+## Constants
+These constants (defined in sqlite3.h) are things such as numeric result codes
from various interfaces (ex: `SQLITE_OK`) or flags passed into functions to
control behavior (ex: `SQLITE_OPEN_READONLY`).
+
+In elisp they are in lowercase and words are separated by "-" instead of
+"_". For example, `SQLITE_OK` would be `sqlite-ok`.
+
+Refer to the [offical site](https://www.sqlite.org/rescode.html)
+for a full list of constants.
+## Functions
+Here I will briefly describe functions available in this module.
+
+Consult the [offical API
documentation](https://www.sqlite.org/c3ref/funclist.html) for details.
+### sqlite3-open
+~~~el
+(sqlite3-open "/path/to/data-file" flags)
+~~~
+Open the database file and return a database handle.
+
+In case of error, the function raises `'db-error` along with a
+corresponding error message.
+
+### sqlite3-close
+~~~el
+(sqlite3-close database-handle)
+~~~
+Close the database file.
+### sqlite3-prepare
+~~~el
+(sqlite3-prepare database-handle sql-statement)
+~~~
+Compile the supplied SQL statement and return a statement handle.
+### sqlite3-finalize
+~~~el
+(sqlite3-finalize statement-handle)
+~~~
+Destroy a prepared statement.
+### sqlite3-step
+~~~el
+(sqlite3-step statement-handle)
+~~~
+Execute a prepared SQL statement. Some of the return codes are:
+
+`sqlite-done` - the statement has finished executing successfully.
+
+`sqlite-row` - if the SQL statement being executed returns any data, then
`sqlite-row` is returned each time a new row of data is ready for processing by
the caller.
+
+### sqlite3-changes
+~~~el
+(sqlite3-changes database-handle)
+~~~
+Return the number of rows modified (for update/delete/insert statements)
+
+### sqlite3-reset
+~~~el
+(sqlite3-reset statement-handle)
+~~~
+Reset a prepared statement. Call this function if you want to re-bind
+the statement to new variables.
+### sqlite3-last-insert-rowid
+~~~el
+(sqlite3-last-insert-rowid database-handle)
+~~~
+Retrieve the last inserted rowid (64 bit).
+
+Notes: Beware that Emacs only supports integers up to 61 bits.
+### sqlite3-get-autocommit
+~~~el
+(sqlite3-get-autocommit database-handle)
+~~~
+Return 1 / 0 if auto-commit mode is ON / OFF.
+### sqlite3-exec
+~~~el
+(sqlite3-exec database-handle sql-statements &optional callback)
+~~~
+The Swiss Army Knife of the API, you can execute multiple SQL statements
+(separated by ";") in a row with just one call.
+
+The callback function, if supplied, is invoked for *each row* and should
accept 3
+ parameters:
+ 1. the first parameter is the number of columns in the current row;
+ 2. the second parameter is the actual data (as a list strings);
+ 3. the third one is a list of column names.
+
+To signal an error condition inside the callback, return `nil`.
+`sqlite3_exec()` will stop the execution and return `sqlite-abort`.
+
+An example of a callback:
+~~~el
+(defun print-row (ncols data names)
+ (cl-loop for i from 0 to (1- ncols) do
+ (message "[%d]%s->%s" elt (ncols names i) (elt data i))
+ (message "--------------------"))
+ t)
+
+(sqlite3-exec dbh "select * from table_a; select * from table b"
+ #'print-row)
+~~~
+More examples:
+~~~el
+;; Update/delete/insert
+(sqlite3-exec dbh "delete from table")
+
+;; Retrieve the metadata of all columns of a table
+(sqlite3-exec dbh "pragma table_info(table)" #'print-row)
+
+;; Transaction support
+(sqlite3-exec dbh "begin")
+....
+(sqlite3-exec dbh "commit")
+...
+(sqlite3-exec dbh "rollback")
+~~~
+### sqlite3-bind-*
+~~~el
+(sqlite3-bind-text statement-handle column-no value)
+(sqlite3-bind-int64 statement-handle column-no value)
+(sqlite3-bind-double statement-handle column-no value)
+(sqlite3-bind-null statement-handle column-no)
+~~~
+The above four functions bind values to a compiled SQL statements.
+
+Please note that column number starts from 1, not 0!
+~~~el
+(sqlite3-bind-parameter-count statement-handle)
+~~~
+The above functions returns the number of SQL parameters of a prepared
+statement.
+~~~el
+(sqlite3-bind-multi statement-handle &rest params)
+~~~
+`sqlite3-bind-multi` is not part of the official API but is provided for
+convenience.
+
+Example:
+~~~el
+(sqlite3-bind-multi stmt 1 "a" 1.555 nil) ;; nil for NULL
+~~~
+### sqlite3-column-*
+These column functions retrieve data from the current row of a
+result set.
+~~~el
+(sqlite3-column-count statement-handle)
+~~~
+Return number of columns in a result set.
+~~~e1
+(sqlite3-column-type statement-handle column-no)
+~~~
+Return the type (`sqlite-integer`, `sqlite-float`, `sqlite-text` or
+`sqlite-null`) of the specified column.
+
+Note: Column number starts from 0.
+~~~el
+(sqlite3-column-text statement-handle column-no)
+(sqlite3-column-int64 statement-handle column-no)
+(sqlite3-column-double statement-handle column-no)
+~~~
+The above functions retrieve data of the specified column.
+
+Note: You can call `sqlite3-column-xxx` on a column even
+if `sqlite3-column-type` returns `sqlite-yyy`: the SQLite3 engine will
+perform the necessary type conversion.
+~~~el
+(sqlite3-fetch statement-handle)
+~~~
+`sqlite3-fetch` is not part of the official API but provided for
+convenience. It returns the current row as a list of values.
+## A Note on Garbage Collection
+Since Emacs's garbage collection is non-deterministic, it would be
+a good idea
+to manually free database/statement handles once they are not needed.
+
+~~~el
+(unwind-protect
+ (progn
+ (setq dbh (sqlite3-open "..."))
+ (setq stmt (sqlite3-prepare dbh "select ..."))
+ (.....))
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh))
+~~~
+## Missing features
+- BLOB support
+- Custom functions written in elisp
+
+## Known Problems
+- SQLite3 supports 64 bit integers but Emacs integers are only 61 bits.
+For integers > 61 bits you can retrieve them as text as a workaround.
+- TEXT fields with embedded NULLs are not supported.
diff --git a/emacs-module.h b/emacs-module.h
new file mode 100644
index 0000000000..f997b9b187
--- /dev/null
+++ b/emacs-module.h
@@ -0,0 +1,204 @@
+/* emacs-module.h - GNU Emacs module API.
+
+Copyright (C) 2015-2017 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef EMACS_MODULE_H
+#define EMACS_MODULE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#if defined __cplusplus && __cplusplus >= 201103L
+# define EMACS_NOEXCEPT noexcept
+#else
+# define EMACS_NOEXCEPT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Current environment. */
+typedef struct emacs_env_25 emacs_env;
+
+/* Opaque pointer representing an Emacs Lisp value.
+ BEWARE: Do not assume NULL is a valid value! */
+typedef struct emacs_value_tag *emacs_value;
+
+enum emacs_arity { emacs_variadic_function = -2 };
+
+/* Struct passed to a module init function (emacs_module_init). */
+struct emacs_runtime
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_runtime_private *private_members;
+
+ /* Return an environment pointer. */
+ emacs_env *(*get_environment) (struct emacs_runtime *ert);
+};
+
+
+/* Function prototype for the module init function. */
+typedef int (*emacs_init_function) (struct emacs_runtime *ert);
+
+/* Function prototype for the module Lisp functions. */
+typedef emacs_value (*emacs_subr) (emacs_env *env, ptrdiff_t nargs,
+ emacs_value args[], void *data);
+
+/* Possible Emacs function call outcomes. */
+enum emacs_funcall_exit
+{
+ /* Function has returned normally. */
+ emacs_funcall_exit_return = 0,
+
+ /* Function has signaled an error using `signal'. */
+ emacs_funcall_exit_signal = 1,
+
+ /* Function has exit using `throw'. */
+ emacs_funcall_exit_throw = 2,
+};
+
+struct emacs_env_25
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_env_private *private_members;
+
+ /* Memory management. */
+
+ emacs_value (*make_global_ref) (emacs_env *env,
+ emacs_value any_reference);
+
+ void (*free_global_ref) (emacs_env *env,
+ emacs_value global_reference);
+
+ /* Non-local exit handling. */
+
+ enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env);
+
+ void (*non_local_exit_clear) (emacs_env *env);
+
+ enum emacs_funcall_exit (*non_local_exit_get)
+ (emacs_env *env,
+ emacs_value *non_local_exit_symbol_out,
+ emacs_value *non_local_exit_data_out);
+
+ void (*non_local_exit_signal) (emacs_env *env,
+ emacs_value non_local_exit_symbol,
+ emacs_value non_local_exit_data);
+
+ void (*non_local_exit_throw) (emacs_env *env,
+ emacs_value tag,
+ emacs_value value);
+
+ /* Function registration. */
+
+ emacs_value (*make_function) (emacs_env *env,
+ ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*function) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value args[],
+ void *)
+ EMACS_NOEXCEPT,
+ const char *documentation,
+ void *data);
+
+ emacs_value (*funcall) (emacs_env *env,
+ emacs_value function,
+ ptrdiff_t nargs,
+ emacs_value args[]);
+
+ emacs_value (*intern) (emacs_env *env,
+ const char *symbol_name);
+
+ /* Type conversion. */
+
+ emacs_value (*type_of) (emacs_env *env,
+ emacs_value value);
+
+ bool (*is_not_nil) (emacs_env *env, emacs_value value);
+
+ bool (*eq) (emacs_env *env, emacs_value a, emacs_value b);
+
+ intmax_t (*extract_integer) (emacs_env *env, emacs_value value);
+
+ emacs_value (*make_integer) (emacs_env *env, intmax_t value);
+
+ double (*extract_float) (emacs_env *env, emacs_value value);
+
+ emacs_value (*make_float) (emacs_env *env, double value);
+
+ /* Copy the content of the Lisp string VALUE to BUFFER as an utf8
+ null-terminated string.
+
+ SIZE must point to the total size of the buffer. If BUFFER is
+ NULL or if SIZE is not big enough, write the required buffer size
+ to SIZE and return false.
+
+ Note that SIZE must include the last null byte (e.g. "abc" needs
+ a buffer of size 4).
+
+ Return true if the string was successfully copied. */
+
+ bool (*copy_string_contents) (emacs_env *env,
+ emacs_value value,
+ char *buffer,
+ ptrdiff_t *size_inout);
+
+ /* Create a Lisp string from a utf8 encoded string. */
+ emacs_value (*make_string) (emacs_env *env,
+ const char *contents, ptrdiff_t length);
+
+ /* Embedded pointer type. */
+ emacs_value (*make_user_ptr) (emacs_env *env,
+ void (*fin) (void *) EMACS_NOEXCEPT,
+ void *ptr);
+
+ void *(*get_user_ptr) (emacs_env *env, emacs_value uptr);
+ void (*set_user_ptr) (emacs_env *env, emacs_value uptr, void *ptr);
+
+ void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr))
+ (void *) EMACS_NOEXCEPT;
+ void (*set_user_finalizer) (emacs_env *env,
+ emacs_value uptr,
+ void (*fin) (void *) EMACS_NOEXCEPT);
+
+ /* Vector functions. */
+ emacs_value (*vec_get) (emacs_env *env, emacs_value vec, ptrdiff_t i);
+
+ void (*vec_set) (emacs_env *env, emacs_value vec, ptrdiff_t i,
+ emacs_value val);
+
+ ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vec);
+};
+
+/* Every module should define a function as follows. */
+extern int emacs_module_init (struct emacs_runtime *ert);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EMACS_MODULE_H */
diff --git a/regression.el b/regression.el
new file mode 100644
index 0000000000..e43b1b973e
--- /dev/null
+++ b/regression.el
@@ -0,0 +1,150 @@
+;; -*- lexical-binding: t -*-
+
+(setq lexical-binding t)
+(add-to-list 'load-path ".")
+(require 'sqlite3-napi)
+(require 'cl)
+
+
+(ert-deftest sqlite3-test-create-db ()
+ (let* ((db-file (make-temp-file "sqlite3"))
+ (dbh (sqlite3-open
+ db-file
+ (logior sqlite-open-create
+ sqlite-open-readwrite)))
+ (stmt))
+ (message "Tests:create-db")
+ (unwind-protect
+ (progn
+ (should (= sqlite-ok (sqlite3-exec dbh "create table temp ( id
integer primary key autoincrement )")))
+ (should-not (= sqlite-ok (sqlite3-exec dbh "create table temp ( id
integer primary key autoincrement )")))
+ (should (= sqlite-ok (sqlite3-exec dbh "insert into temp values
(NULL)")))
+ (setq stmt (sqlite3-prepare dbh "select count(*) from temp"))
+ (should (= sqlite-row (sqlite3-step stmt)))
+ (should (= 1 (sqlite3-column-count stmt)))
+ (should (= 1 (sqlite3-column-int64 stmt 0))))
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh)
+ (delete-file db-file))))
+
+
+(ert-deftest sqlite3-test-memory-db ()
+ (let* ((dbh (sqlite3-open
+ ":memory:"
+ (logior sqlite-open-create
+ sqlite-open-readwrite)))
+ (stmt))
+ (message "Test:memory-db")
+ (unwind-protect
+ (progn
+ (should (= sqlite-ok (sqlite3-exec dbh "create table temp ( id
integer primary key autoincrement )")))
+ (should (= sqlite-ok (sqlite3-exec dbh "insert into temp values
(NULL)")))
+ (setq stmt (sqlite3-prepare dbh "select count(*) from temp"))
+ (should (= sqlite-row (sqlite3-step stmt)))
+ (should (= 1 (sqlite3-column-count stmt)))
+ (should (= 1 (sqlite3-column-int64 stmt 0))))
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh))))
+
+
+(ert-deftest sqlite3-test-temp-db ()
+ (let* ((dbh (sqlite3-open
+ ""
+ (logior sqlite-open-create
+ sqlite-open-readwrite)))
+ (stmt))
+ (message "Test:temp-db")
+ (unwind-protect
+ (progn
+ (should (= sqlite-ok (sqlite3-exec dbh "create table test ( id
integer primary key autoincrement )")))
+ (should (= sqlite-ok (sqlite3-exec dbh "insert into test values
(NULL)")))
+ (setq stmt (sqlite3-prepare dbh "select count(*) from test"))
+ (should (= sqlite-row (sqlite3-step stmt)))
+ (should (= 1 (sqlite3-column-count stmt)))
+ (should (= 1 (sqlite3-column-int64 stmt 0))))
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh))))
+
+(ert-deftest sqlite3-test-bulk-ops ()
+ (let ((dbh)
+ (stmt))
+ (message "Test:bulk-ops")
+ (unwind-protect
+ (progn
+ (setq dbh (sqlite3-open "" (logior
+ sqlite-open-readwrite
+ sqlite-open-create)))
+ (should (= sqlite-ok (sqlite3-exec dbh "create table test ( id
integer primary key autoincrement, rand integer not null )")))
+ (should (setq stmt (sqlite3-prepare dbh "insert into test values (?,
?)")))
+ (should (= 2 (sqlite3-bind-parameter-count stmt)))
+ (sqlite3-exec dbh "begin")
+ (cl-loop for i from 1 to 100000 do
+ (should (= sqlite-ok (sqlite3-bind-null stmt 1)))
+ (should (= sqlite-ok (sqlite3-bind-int64 stmt 2 (random))))
+ (should (= sqlite-done (sqlite3-step stmt)))
+ (should (= sqlite-ok (sqlite3-reset stmt))))
+ (should (= 100000 (sqlite3-last-insert-rowid dbh)))
+ (should (= sqlite-ok (sqlite3-exec dbh "update test set rand = 0")))
+ (should (= 0 (sqlite3-get-autocommit dbh)))
+ (should (= 100000 (sqlite3-changes dbh)))
+ (should (= sqlite-ok (sqlite3-exec dbh "delete from test where id >
5000")))
+ (should (= 95000 (sqlite3-changes dbh)))
+ (sqlite3-exec dbh "commit"))
+ (sqlite3-finalize stmt))
+ (sqlite3-close dbh)))
+
+(ert-deftest sqlite3-test-datatypes ()
+ (let ((dbh)
+ (stmt)
+ (most-pos-double (ldexp 0.9999999999999999 1024))
+ (most-neg-double (ldexp 0.5 -1021))
+ (big-str (make-string 10000000 ?1))
+ (utf8-str "一二三四五六七"))
+ (message "Test:datatypes")
+ (unwind-protect
+ (progn
+ (setq dbh (sqlite3-open "" (logior sqlite-open-readwrite
+ sqlite-open-create)))
+ (should (= sqlite-ok (sqlite3-exec dbh "create table temp (pos_int
integer, neg_int integer, pos_flt real, neg_flt real, big_str text, utf8_str
text, more_text text)")))
+ (should (setq stmt (sqlite3-prepare dbh "insert into temp values
(?,?,?,?,?,?,?)")))
+ (should (= sqlite-ok (sqlite3-bind-int64 stmt 1
most-positive-fixnum)))
+ (should (= sqlite-ok (sqlite3-bind-int64 stmt 2
most-negative-fixnum)))
+ (should (= sqlite-ok (sqlite3-bind-double stmt 3 most-pos-double)))
+ (should (= sqlite-ok (sqlite3-bind-double stmt 4 most-neg-double)))
+ (should (= sqlite-ok (sqlite3-bind-text stmt 5 big-str)))
+ (should (= sqlite-ok (sqlite3-bind-text stmt 6 utf8-str)))
+ (should (= sqlite-ok (sqlite3-bind-null stmt 7)))
+ (should (= sqlite-done (sqlite3-step stmt)))
+ (should (setq stmt (sqlite3-prepare dbh "select * from temp")))
+ (should (= sqlite-row (sqlite3-step stmt)))
+ (should (= most-positive-fixnum (sqlite3-column-int64 stmt 0)))
+ (should (= most-negative-fixnum (sqlite3-column-int64 stmt 1)))
+ (should (= most-pos-double (sqlite3-column-double stmt 2)))
+ (should (= most-neg-double (sqlite3-column-double stmt 3)))
+ (should (string= big-str (sqlite3-column-text stmt 4)))
+ (should (string= utf8-str (sqlite3-column-text stmt 5)))
+ (should (= sqlite-null (sqlite3-column-type stmt 6))))
+ (sqlite3-finalize stmt)
+ (sqlite3-close dbh))))
+
+(sqlite3-set-log-level 3)
+(ert "^sqlite3-test")
+(garbage-collect)
+
+
+;; update - bind
+;; delete - bind
+;; delete entire table
+
+;; transaction
+;; commit
+;; rollback
+
+;; drop table
+;; build index
+;; drop index
+;; add column
+
+;; select
+
+;; pragma
diff --git a/sqlite3-napi-module.c b/sqlite3-napi-module.c
new file mode 100644
index 0000000000..91f6b113d7
--- /dev/null
+++ b/sqlite3-napi-module.c
@@ -0,0 +1,975 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sqlite3.h>
+#include <float.h>
+#include "emacs-module.h"
+
+
+int plugin_is_GPL_compatible;
+
+#define NON_LOCAL_EXIT_CHECK(env) \
+ if ((env)->non_local_exit_check(env) != emacs_funcall_exit_return) { \
+ return (env)->intern((env), "nil"); \
+ }
+#define SYM(env, sym) (env)->intern((env), sym)
+#define IS_INTEGER(env, val) \
+ (env)->type_of((env), (val)) == (env)->intern((env), "integer")
+#define IS_FLOAT(env, val) \
+ (env)->type_of((env), (val)) == (env)->intern((env), "float")
+#define IS_STRING(env, val) \
+ (env)->type_of((env), (val)) == (env)->intern((env), "string")
+
+#define WARN(env, ...) message(env, SQLITE3_LOG_LEVEL_WARN, __VA_ARGS__)
+#define DEBUG(env, ...) message(env, SQLITE3_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define ERROR(env, ...) message(env, SQLITE3_LOG_LEVEL_ERROR, __VA_ARGS__)
+#define INFO(env, ...) message(env, SQLITE3_LOG_LEVEL_INFO, __VA_ARGS__)
+
+#define SQLITE3_MAX_LOG_BUF 1000
+
+static int SQLITE3_LOG_LEVEL_DEBUG = 0;
+static int SQLITE3_LOG_LEVEL_INFO = 1;
+static int SQLITE3_LOG_LEVEL_WARN = 2;
+static int SQLITE3_LOG_LEVEL_ERROR = 3;
+static int sqlite3_napi_log_level;
+
+int symbol_value_as_int(emacs_env *env,
+ emacs_value sym,
+ int defaul) {
+ emacs_value v = env->funcall(env, SYM(env, "symbol-value"), 1, &sym);
+ if (IS_INTEGER(env, v))
+ return env->extract_integer(env, v);
+ return defaul;
+}
+
+#if 0
+static void message(emacs_env *env, int log_level, const char *fmt, ...) {
+ if (log_level < sqlite3_napi_log_level)
+ return;
+
+ va_list args;
+ static char log_buf[SQLITE3_MAX_LOG_BUF];
+
+ va_start(args, fmt);
+ vsnprintf(log_buf, SQLITE3_MAX_LOG_BUF, fmt, args);
+ va_end(args);
+
+ static const char *LOG_LEVEL_DESC[] = {
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR"
+ };
+
+ static char new_log_buf[SQLITE3_MAX_LOG_BUF];
+ snprintf(new_log_buf, SQLITE3_MAX_LOG_BUF, "[%s] %s",
+ LOG_LEVEL_DESC[log_level], log_buf);
+ emacs_value msg_func = SYM(env, "message");
+ emacs_value arg = env->make_string(env, new_log_buf,
+ strlen(new_log_buf));
+ env->funcall(env, msg_func, 1, &arg);
+}
+#endif
+
+static void message(emacs_env *env, int log_level, const char *fmt, ...) {
+ (void)env;
+
+ if (log_level < sqlite3_napi_log_level)
+ return;
+
+ static const char *LOG_LEVEL_DESC[] = {
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR"
+ };
+ fprintf(stderr, "[%s] ", LOG_LEVEL_DESC[log_level]);
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, "\n");
+}
+
+void signal_error(emacs_env *env, const char *symbol, const char *msg) {
+ emacs_value signal = SYM(env, symbol);
+ emacs_value errmsg = env->make_string(env, msg, strlen(msg));
+ env->non_local_exit_signal(env, signal, errmsg);
+}
+
+/* Extract and copy string contents from function parameters */
+int extract_string_arg(emacs_env *env, emacs_value arg, char **str) {
+ ptrdiff_t size = 0;
+ if (!env->copy_string_contents(env, arg, NULL, &size))
+ return 1;
+
+ *str = malloc(size);
+ if (!env->copy_string_contents(env, arg, *str, &size)) {
+ free(*str);
+ *str = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/* Create a LISP function callable from within Emacs */
+void bind_func(emacs_env *env, const char *name, ptrdiff_t min,
+ ptrdiff_t max,
+ emacs_value (*function) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value args[],
+ void *) EMACS_NOEXCEPT,
+ const char *doc) {
+ emacs_value fset = SYM(env, "fset");
+ emacs_value args[2];
+
+ args[0] = SYM(env, name);
+ args[1] = env->make_function(env, min, max, function, doc, 0);
+ env->funcall(env, fset, 2, args);
+}
+
+static void sqlite3_db_gc(void *ptr) {
+ INFO(0, "%s: entered", __func__);
+
+ if (ptr) {
+ INFO(0, "%s: non-null dbh", __func__);
+ sqlite3_close((sqlite3 *)ptr);
+ }
+}
+
+static void sqlite3_stmt_gc(void *ptr) {
+ INFO(0, "%s: entered", __func__);
+
+ if (ptr) {
+ INFO(0, "%s: non-null stmt", __func__);
+ sqlite3_finalize((sqlite3_stmt *)ptr);
+ }
+}
+
+/* bind_*() functions:
+ Bind SQL parameters after the SQL is prepared (compiled).
+*/
+static emacs_value sqlite3_napi_bind_null(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ /* User passed a nil stmt */
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+ /* if (env->non_local_exit_check(env) != emacs_funcall_exit_return) */
+ /* return SYM(env, "nil"); */
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+ /* if (env->non_local_exit_check(env) != emacs_funcall_exit_return) { */
+ /* return SYM(env, "nil"); */
+ /* } */
+
+ return env->make_integer(env, sqlite3_bind_null(stmt, col));
+}
+
+static emacs_value sqlite3_napi_bind_double(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ double val = env->extract_float(env, args[2]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_bind_double(stmt, col, val));
+}
+
+static emacs_value sqlite3_napi_bind_parameter_count(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_bind_parameter_count(stmt));
+}
+
+static emacs_value sqlite3_napi_bind_int64(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+
+ intmax_t val = env->extract_integer(env, args[2]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ /* DEBUG(env, "%s: col %d, val %d", __func__, col, val); */
+ return env->make_integer(env, sqlite3_bind_int64(stmt, col, val));
+}
+
+static emacs_value sqlite3_napi_bind_text(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ char *txt;
+ if (extract_string_arg(env, args[2], &txt)) {
+ return SYM(env, "nil");
+ }
+
+ DEBUG(env, "%s: [%s] to col %d", __func__, txt, col);
+ int rv = sqlite3_bind_text(stmt, col, txt, -1, SQLITE_TRANSIENT);
+ free(txt);
+ return env->make_integer(env, rv);
+}
+
+static emacs_value sqlite3_napi_bind_multi(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ int rv;
+ for (int i = 1; i < n; i++) {
+ char *txt = 0;
+
+ //emacs_value type = env->type_of(env, args[i]);
+ if (IS_INTEGER(env, args[i])) {
+ rv = sqlite3_bind_int64(stmt, i, env->extract_integer(env, args[i]));
+ NON_LOCAL_EXIT_CHECK(env);
+ } else if (IS_FLOAT(env, args[i])) {
+ rv = sqlite3_bind_double(stmt, i, env->extract_float(env, args[i]));
+ NON_LOCAL_EXIT_CHECK(env);
+ } else if (IS_STRING(env, args[i])) {
+ extract_string_arg(env, args[i], &txt);
+ rv = sqlite3_bind_text(stmt, i, txt, -1, SQLITE_TRANSIENT);
+ free(txt);
+ NON_LOCAL_EXIT_CHECK(env);
+ } else if (args[i] == SYM(env, "nil")) {
+ rv = sqlite3_bind_null(stmt, i);
+ NON_LOCAL_EXIT_CHECK(env);
+ } else {
+ WARN(env, "%s: arg %d is of unknown type", __func__, i);
+ rv = SQLITE_MISUSE;
+ }
+ if (rv != SQLITE_OK) {
+ WARN(env, "%s: ERROR CODE = %d", __func__, rv);
+ sqlite3_reset(stmt);
+ break;
+ }
+ }
+
+ /* message(env, "bind_text [%s] to col %d", txt, col); */
+
+ return env->make_integer(env, rv);
+}
+
+static emacs_value sqlite3_napi_column_name(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ const char *name = sqlite3_column_name(stmt, col);
+ return env->make_string(env, name, strlen(name));
+}
+
+static emacs_value sqlite3_napi_column_text(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ int size = sqlite3_column_bytes(stmt, col);
+ return env->make_string(env,
+ (const char *)sqlite3_column_text(stmt, col),
+ size);
+}
+
+static emacs_value sqlite3_napi_column_int64(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, (intmax_t)sqlite3_column_int64(stmt, col));
+}
+
+static emacs_value sqlite3_napi_column_double(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_float(env, sqlite3_column_double(stmt, col));
+}
+
+static emacs_value sqlite3_napi_column_type(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // TODO:
+ // should signal an error instead of return SYM(env, "nil")??
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // The column no.
+ int col = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_column_type(stmt, col));
+}
+
+static emacs_value sqlite3_napi_changes(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: database handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_changes(dbh));
+}
+
+static emacs_value sqlite3_napi_step(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env)
+
+ return env->make_integer(env, sqlite3_step(stmt));
+}
+
+static emacs_value sqlite3_napi_reset(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "reset(): statement handle is nil");
+ return SYM(env, "nil");
+ }
+
+ // Exrtract sqlite3 db struct
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_reset(stmt));
+}
+
+static emacs_value sqlite3_napi_column_count(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, sqlite3_column_count(stmt));
+}
+
+static emacs_value sqlite3_napi_fetch(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: statement handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ /* Create a list to store the results */
+ int ncols = sqlite3_column_count(stmt);
+ emacs_value *elts = malloc(sizeof(emacs_value)*ncols);
+ for (int i = 0; i < ncols; i++) {
+ switch(sqlite3_column_type(stmt, i)) {
+ case SQLITE_INTEGER:
+ elts[i] = env->make_integer(env, sqlite3_column_int64(stmt, i));
+ break;
+ case SQLITE_FLOAT:
+ elts[i] = env->make_float(env, sqlite3_column_double(stmt, i));
+ break;
+ case SQLITE_TEXT:
+ elts[i] = env->make_string(
+ env,
+ (const char *)sqlite3_column_text(stmt, i),
+ sqlite3_column_bytes(stmt, i));
+ break;
+ default:
+ elts[i] = SYM(env, "nil");
+ }
+ }
+ emacs_value list = SYM(env, "list");
+ emacs_value res = env->funcall(env, list, ncols, elts);
+ free(elts);
+ return res;
+}
+
+static emacs_value sqlite3_napi_prepare(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Exrtract sqlite3 db struct
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: database handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ // SQL statement to be prepared
+ char *sql_txt;
+ if (extract_string_arg(env, args[1], &sql_txt)) {
+ return SYM(env, "nil");
+ }
+
+ // Prepare
+ sqlite3_stmt *stmt;
+ const char *tail;
+ int rv = sqlite3_prepare_v2(dbh, sql_txt, -1, &stmt, &tail);
+ INFO(env, "%s: statement prepared (rv=%d)", __func__, rv);
+
+ free(sql_txt);
+ if (rv != SQLITE_OK) {
+ char buf[SQLITE3_MAX_LOG_BUF];
+ snprintf(buf, SQLITE3_MAX_LOG_BUF,
+ "prepare(): sqlite3_prepare_v2() returned %d", rv);
+ signal_error(env, "db-error", buf);
+ return SYM(env, "nil");
+ }
+ return env->make_user_ptr(env, sqlite3_stmt_gc, stmt);
+}
+
+static emacs_value sqlite3_napi_get_autocommit(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ /* User passed a nil stmt */
+ if (!env->is_not_nil(env, args[0]))
+ return SYM(env, "nil");
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ INFO(env, "%s: entered", __func__);
+ return env->make_integer(env, sqlite3_get_autocommit(dbh));
+}
+
+/* Small struct for passing data from sqlite3_exec() to exec_callback() */
+struct func_env {
+ emacs_env *env;
+ emacs_value callback;
+};
+
+/* this #define is only used in exec_callback() */
+#define NON_LOCAL_EXIT_CHECK_AND_CLEANUP \
+ if (env->non_local_exit_check(env) != emacs_funcall_exit_return) { \
+ free(data_args); \
+ free(col_args); \
+ return 1; \
+}
+
+static int exec_callback(void *data, int ncols,
+ char **col_data, char **col_names) {
+ struct func_env *fe = (struct func_env *)data;
+
+ emacs_env *env = fe->env;
+
+ /* Build up two lists and pass them to the LISP callback */
+ emacs_value *data_args = malloc(sizeof(emacs_value)*ncols);
+ emacs_value *col_args = malloc(sizeof(emacs_value)*ncols);
+
+ for (int i = 0; i < ncols; i++) {
+ if (col_data[i])
+ data_args[i] = env->make_string(env, col_data[i],
+ strlen(col_data[i]));
+ else
+ data_args[i] = SYM(env, "nil");
+ NON_LOCAL_EXIT_CHECK_AND_CLEANUP;
+
+ col_args[i] = env->make_string(env, col_names[i],
+ strlen(col_names[i]));
+ NON_LOCAL_EXIT_CHECK_AND_CLEANUP;
+ }
+
+ /* equivalent to (list "a" "b" "c" ....) */
+ emacs_value args[3];
+ args[0] = env->make_integer(env, ncols);
+ NON_LOCAL_EXIT_CHECK_AND_CLEANUP;
+ args[1] = env->funcall(env, SYM(env, "list"), ncols, data_args);
+ NON_LOCAL_EXIT_CHECK_AND_CLEANUP;
+ args[2] = env->funcall(env, SYM(env, "list"), ncols, col_args);
+ NON_LOCAL_EXIT_CHECK_AND_CLEANUP;
+
+ emacs_value v = env->funcall(env, fe->callback, 3, args);
+ free(data_args);
+ free(col_args);
+
+ if (env->is_not_nil(env, v))
+ return 0;
+ return 1;
+}
+
+static emacs_value sqlite3_napi_exec(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+
+ /* User passed a nil dbh */
+ if (!env->is_not_nil(env, args[0]))
+ return SYM(env, "nil");
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ char *sql_txt;
+ if (extract_string_arg(env, args[1], &sql_txt)) {
+ return SYM(env, "nil");
+ }
+
+ char *errmsg;
+ int rv;
+ if (n == 3) {
+ struct func_env fe = { env, args[2] };
+ rv = sqlite3_exec(dbh, sql_txt, exec_callback, (void *)&fe, &errmsg);
+ } else {
+ rv = sqlite3_exec(dbh, sql_txt, 0, 0, &errmsg);
+ }
+
+ if (rv != SQLITE_OK) {
+ ERROR(env, "%s returned %d [%s]", __func__, rv, errmsg);
+ }
+ if (errmsg)
+ sqlite3_free(errmsg);
+
+ return env->make_integer(env, rv);
+}
+
+static emacs_value sqlite3_napi_finalize(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ /* User passed a nil stmt */
+ if (!env->is_not_nil(env, args[0]))
+ return SYM(env, "nil");
+
+ sqlite3_stmt *stmt = (sqlite3_stmt *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ INFO(env, "%s: entered", __func__);
+ sqlite3_finalize(stmt);
+ env->set_user_ptr(env, args[0], 0);
+ return SYM(env, "nil");
+}
+
+
+static emacs_value sqlite3_napi_close(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ /* nil database handle */
+ if (!env->is_not_nil(env, args[0]))
+ return SYM(env, "nil");
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ INFO(env, "%s: entered", __func__);
+ sqlite3_close(dbh);
+ env->set_user_ptr(env, args[0], 0);
+ return SYM(env, "nil");
+}
+
+static emacs_value sqlite3_napi_last_insert_rowid(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ if (!env->is_not_nil(env, args[0])) {
+ WARN(env, "%s: database handle is nil", __func__);
+ return SYM(env, "nil");
+ }
+
+ sqlite3 *dbh = (sqlite3 *)env->get_user_ptr(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ return env->make_integer(env, (intmax_t)sqlite3_last_insert_rowid(dbh));
+}
+
+static emacs_value sqlite3_napi_set_log_level(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ int log_level = env->extract_integer(env, args[0]);
+ NON_LOCAL_EXIT_CHECK(env);
+ sqlite3_napi_log_level = log_level;
+ return SYM(env, "nil");
+}
+
+#if 0
+static emacs_value sqlite3_napi_test(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ //return SYM(env, "nil");
+ emacs_value *fargs = malloc(sizeof(emacs_value)*2);
+ fargs[0] = env->make_integer(env, 1);
+ fargs[1] = env->make_integer(env, 99);
+ return env->funcall(env, args[0], 2, fargs);
+}
+#endif
+
+static emacs_value sqlite3_napi_open(
+ emacs_env *env,
+ ptrdiff_t n,
+ emacs_value *args,
+ void *ptr) {
+ (void)ptr;
+ (void)n;
+
+ // Filename
+ char *db_file = 0;
+ if (extract_string_arg(env, args[0], &db_file)) {
+ WARN(env, "%s: extract_string_arg return non-zero", __func__);
+ return SYM(env, "nil");
+ }
+
+ // FLAGS
+ int flags = env->extract_integer(env, args[1]);
+ NON_LOCAL_EXIT_CHECK(env);
+
+ sqlite3 *dbh;
+ int rv = sqlite3_open_v2(db_file, &dbh, flags, 0);
+ INFO(env, "%s: file=%s, flags=%d, rv=%d", __func__, db_file, flags, rv);
+ free(db_file);
+
+ if (rv != SQLITE_OK) {
+ // TODO: more descriptive error message (with error code)
+ signal_error(env, "db-error", "failed to open DB file");
+ return SYM(env, "nil");
+ }
+
+ return env->make_user_ptr(env, sqlite3_db_gc, dbh);
+}
+
+int emacs_module_init(struct emacs_runtime *ert) {
+ emacs_env *env = ert->get_environment(ert);
+
+ struct lisp_func {
+ const char *lisp_func_name;
+ ptrdiff_t min_arity;
+ ptrdiff_t max_arity;
+ emacs_value (*function) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value args[],
+ void *) EMACS_NOEXCEPT;
+ const char *documentation;
+ };
+
+ struct lisp_func all_funcs[] = {
+ { "sqlite3-open", 1, 2, sqlite3_napi_open,
+ "Open a SQLite3 database." },
+ { "sqlite3-close", 1, 1, sqlite3_napi_close,
+ "Close a SQLite3 database." },
+ { "sqlite3-prepare", 2, 2, sqlite3_napi_prepare,
+ "Prepare (compile) a SQL statement." },
+ { "sqlite3-finalize", 1, 1, sqlite3_napi_finalize,
+ "Destroy a prepared statement." },
+ { "sqlite3-changes", 1, 1, sqlite3_napi_changes,
+ "Count the number of rows modified." },
+ { "sqlite3-step", 1, 1, sqlite3_napi_step,
+ "Evaluate a SQL statement." },
+ { "sqlite3-reset", 1, 1, sqlite3_napi_reset,
+ "Reset a prepared SQL statement." },
+ { "sqlite3-last-insert-rowid", 1, 1, sqlite3_napi_last_insert_rowid,
+ "Return last insert rowid." },
+ { "sqlite3-get-autocommit", 1, 1, sqlite3_napi_get_autocommit,
+ "Test for auto-commit mode." },
+ { "sqlite3-exec", 2, 3, sqlite3_napi_exec,
+ "One-step query execution interface." },
+ { "sqlite3-set-log-level", 1, 1, sqlite3_napi_set_log_level,
+ "Set log level (DEBUG 0, INFO 1, WARN 2, ERROR 3, NOLOG 100)." },
+
+ /* bind interface */
+ { "sqlite3-bind-text", 3, 3, sqlite3_napi_bind_text,
+ "Bind text to a prepared SQL statement." },
+ { "sqlite3-bind-int64", 3, 3, sqlite3_napi_bind_int64,
+ "Bind int64 to a prepared SQL statement." },
+ { "sqlite3-bind-double", 3, 3, sqlite3_napi_bind_double,
+ "Bind double to a prepared SQL statement." },
+ { "sqlite3-bind-null", 2, 2, sqlite3_napi_bind_null,
+ "Bind NULL to a prepared SQL statement." },
+ { "sqlite3-bind-parameter-count", 1, 1,
+ sqlite3_napi_bind_parameter_count,
+ "Return the number of SQL parameters." },
+ { "sqlite3-bind-multi", 1, 127, sqlite3_napi_bind_multi,
+ "Bind multiple parameters to a prepared SQL statement." },
+
+ /* Result */
+ { "sqlite3-column-count", 1, 1, sqlite3_napi_column_count,
+ "Return the number of rows in a result set." },
+ { "sqlite3-column-name", 2, 2, sqlite3_napi_column_name,
+ "Return the name of a column." },
+ { "sqlite3-column-type", 2, 2, sqlite3_napi_column_type,
+ "Return the datatype of a column." },
+ { "sqlite3-column-text", 2, 2, sqlite3_napi_column_text,
+ "Return text data of a column." },
+ { "sqlite3-column-int64", 2, 2, sqlite3_napi_column_int64,
+ "Return int64 data of a column." },
+ { "sqlite3-column-double", 2, 2, sqlite3_napi_column_double,
+ "Return double data of a column." },
+ { "sqlite3-fetch", 1, 1, sqlite3_napi_fetch,
+ "Return a row as a list." },
+
+ { NULL, 0, 0, NULL, NULL }
+ };
+
+ for (int i = 0; all_funcs[i].lisp_func_name != NULL; i++) {
+ bind_func(env,
+ all_funcs[i].lisp_func_name,
+ all_funcs[i].min_arity,
+ all_funcs[i].max_arity,
+ all_funcs[i].function,
+ all_funcs[i].documentation);
+ }
+ sqlite3_napi_log_level = SQLITE3_LOG_LEVEL_ERROR;
+
+ /* (provide 'sqlite3-module ) */
+ emacs_value provide = SYM(env, "provide");
+ emacs_value mod = SYM(env, "sqlite3-napi-module");
+ env->funcall(env, provide, 1, &mod);
+ return 0;
+}
diff --git a/sqlite3-napi.el b/sqlite3-napi.el
new file mode 100644
index 0000000000..b41139a767
--- /dev/null
+++ b/sqlite3-napi.el
@@ -0,0 +1,368 @@
+(require 'sqlite3-napi-module)
+(defconst sqlite-version "3.16.0")
+(defconst sqlite-version-number 3016000)
+(defconst sqlite-source-id "2016-11-04 19:09:39
0e5ffd9123d6d2d2b8f3701e8a73cc98a3a7ff5f")
+(defconst sqlite-ok 0)
+(defconst sqlite-error 1)
+(defconst sqlite-internal 2)
+(defconst sqlite-perm 3)
+(defconst sqlite-abort 4)
+(defconst sqlite-busy 5)
+(defconst sqlite-locked 6)
+(defconst sqlite-nomem 7)
+(defconst sqlite-readonly 8)
+(defconst sqlite-interrupt 9)
+(defconst sqlite-ioerr 10)
+(defconst sqlite-corrupt 11)
+(defconst sqlite-notfound 12)
+(defconst sqlite-full 13)
+(defconst sqlite-cantopen 14)
+(defconst sqlite-protocol 15)
+(defconst sqlite-empty 16)
+(defconst sqlite-schema 17)
+(defconst sqlite-toobig 18)
+(defconst sqlite-constraint 19)
+(defconst sqlite-mismatch 20)
+(defconst sqlite-misuse 21)
+(defconst sqlite-nolfs 22)
+(defconst sqlite-auth 23)
+(defconst sqlite-format 24)
+(defconst sqlite-range 25)
+(defconst sqlite-notadb 26)
+(defconst sqlite-notice 27)
+(defconst sqlite-warning 28)
+(defconst sqlite-row 100)
+(defconst sqlite-done 101)
+(defconst sqlite-ioerr-read 266)
+(defconst sqlite-ioerr-short-read 522)
+(defconst sqlite-ioerr-write 778)
+(defconst sqlite-ioerr-fsync 1034)
+(defconst sqlite-ioerr-dir-fsync 1290)
+(defconst sqlite-ioerr-truncate 1546)
+(defconst sqlite-ioerr-fstat 1802)
+(defconst sqlite-ioerr-unlock 2058)
+(defconst sqlite-ioerr-rdlock 2314)
+(defconst sqlite-ioerr-delete 2570)
+(defconst sqlite-ioerr-blocked 2826)
+(defconst sqlite-ioerr-nomem 3082)
+(defconst sqlite-ioerr-access 3338)
+(defconst sqlite-ioerr-checkreservedlock 3594)
+(defconst sqlite-ioerr-lock 3850)
+(defconst sqlite-ioerr-close 4106)
+(defconst sqlite-ioerr-dir-close 4362)
+(defconst sqlite-ioerr-shmopen 4618)
+(defconst sqlite-ioerr-shmsize 4874)
+(defconst sqlite-ioerr-shmlock 5130)
+(defconst sqlite-ioerr-shmmap 5386)
+(defconst sqlite-ioerr-seek 5642)
+(defconst sqlite-ioerr-delete-noent 5898)
+(defconst sqlite-ioerr-mmap 6154)
+(defconst sqlite-ioerr-gettemppath 6410)
+(defconst sqlite-ioerr-convpath 6666)
+(defconst sqlite-ioerr-vnode 6922)
+(defconst sqlite-ioerr-auth 7178)
+(defconst sqlite-locked-sharedcache 262)
+(defconst sqlite-busy-recovery 261)
+(defconst sqlite-busy-snapshot 517)
+(defconst sqlite-cantopen-notempdir 270)
+(defconst sqlite-cantopen-isdir 526)
+(defconst sqlite-cantopen-fullpath 782)
+(defconst sqlite-cantopen-convpath 1038)
+(defconst sqlite-corrupt-vtab 267)
+(defconst sqlite-readonly-recovery 264)
+(defconst sqlite-readonly-cantlock 520)
+(defconst sqlite-readonly-rollback 776)
+(defconst sqlite-readonly-dbmoved 1032)
+(defconst sqlite-abort-rollback 516)
+(defconst sqlite-constraint-check 275)
+(defconst sqlite-constraint-commithook 531)
+(defconst sqlite-constraint-foreignkey 787)
+(defconst sqlite-constraint-function 1043)
+(defconst sqlite-constraint-notnull 1299)
+(defconst sqlite-constraint-primarykey 1555)
+(defconst sqlite-constraint-trigger 1811)
+(defconst sqlite-constraint-unique 2067)
+(defconst sqlite-constraint-vtab 2323)
+(defconst sqlite-constraint-rowid 2579)
+(defconst sqlite-notice-recover-wal 283)
+(defconst sqlite-notice-recover-rollback 539)
+(defconst sqlite-warning-autoindex 284)
+(defconst sqlite-auth-user 279)
+(defconst sqlite-ok-load-permanently 256)
+(defconst sqlite-open-readonly 1)
+(defconst sqlite-open-readwrite 2)
+(defconst sqlite-open-create 4)
+(defconst sqlite-open-deleteonclose 8)
+(defconst sqlite-open-exclusive 16)
+(defconst sqlite-open-autoproxy 32)
+(defconst sqlite-open-uri 64)
+(defconst sqlite-open-memory 128)
+(defconst sqlite-open-main-db 256)
+(defconst sqlite-open-temp-db 512)
+(defconst sqlite-open-transient-db 1024)
+(defconst sqlite-open-main-journal 2048)
+(defconst sqlite-open-temp-journal 4096)
+(defconst sqlite-open-subjournal 8192)
+(defconst sqlite-open-master-journal 16384)
+(defconst sqlite-open-nomutex 32768)
+(defconst sqlite-open-fullmutex 65536)
+(defconst sqlite-open-sharedcache 131072)
+(defconst sqlite-open-privatecache 262144)
+(defconst sqlite-open-wal 524288)
+(defconst sqlite-open-fileprotection-complete 1048576)
+(defconst sqlite-open-fileprotection-completeunlessopen 2097152)
+(defconst sqlite-open-fileprotection-completeuntilfirstuserauthentication
3145728)
+(defconst sqlite-open-fileprotection-none 4194304)
+(defconst sqlite-open-fileprotection-mask 7340032)
+(defconst sqlite-iocap-atomic 1)
+(defconst sqlite-iocap-atomic512 2)
+(defconst sqlite-iocap-atomic1k 4)
+(defconst sqlite-iocap-atomic2k 8)
+(defconst sqlite-iocap-atomic4k 16)
+(defconst sqlite-iocap-atomic8k 32)
+(defconst sqlite-iocap-atomic16k 64)
+(defconst sqlite-iocap-atomic32k 128)
+(defconst sqlite-iocap-atomic64k 256)
+(defconst sqlite-iocap-safe-append 512)
+(defconst sqlite-iocap-sequential 1024)
+(defconst sqlite-iocap-undeletable-when-open 2048)
+(defconst sqlite-iocap-powersafe-overwrite 4096)
+(defconst sqlite-iocap-immutable 8192)
+(defconst sqlite-lock-none 0)
+(defconst sqlite-lock-shared 1)
+(defconst sqlite-lock-reserved 2)
+(defconst sqlite-lock-pending 3)
+(defconst sqlite-lock-exclusive 4)
+(defconst sqlite-sync-normal 2)
+(defconst sqlite-sync-full 3)
+(defconst sqlite-sync-dataonly 16)
+(defconst sqlite-fcntl-lockstate 1)
+(defconst sqlite-fcntl-get-lockproxyfile 2)
+(defconst sqlite-fcntl-set-lockproxyfile 3)
+(defconst sqlite-fcntl-last-errno 4)
+(defconst sqlite-fcntl-size-hint 5)
+(defconst sqlite-fcntl-chunk-size 6)
+(defconst sqlite-fcntl-file-pointer 7)
+(defconst sqlite-fcntl-sync-omitted 8)
+(defconst sqlite-fcntl-win32-av-retry 9)
+(defconst sqlite-fcntl-persist-wal 10)
+(defconst sqlite-fcntl-overwrite 11)
+(defconst sqlite-fcntl-vfsname 12)
+(defconst sqlite-fcntl-powersafe-overwrite 13)
+(defconst sqlite-fcntl-pragma 14)
+(defconst sqlite-fcntl-busyhandler 15)
+(defconst sqlite-fcntl-tempfilename 16)
+(defconst sqlite-fcntl-mmap-size 18)
+(defconst sqlite-fcntl-trace 19)
+(defconst sqlite-fcntl-has-moved 20)
+(defconst sqlite-fcntl-sync 21)
+(defconst sqlite-fcntl-commit-phasetwo 22)
+(defconst sqlite-fcntl-win32-set-handle 23)
+(defconst sqlite-fcntl-wal-block 24)
+(defconst sqlite-fcntl-zipvfs 25)
+(defconst sqlite-fcntl-rbu 26)
+(defconst sqlite-fcntl-vfs-pointer 27)
+(defconst sqlite-fcntl-journal-pointer 28)
+(defconst sqlite-fcntl-win32-get-handle 29)
+(defconst sqlite-fcntl-pdb 30)
+(defconst sqlite-get-lockproxyfile 2)
+(defconst sqlite-set-lockproxyfile 3)
+(defconst sqlite-last-errno 4)
+(defconst sqlite-access-exists 0)
+(defconst sqlite-access-readwrite 1)
+(defconst sqlite-access-read 2)
+(defconst sqlite-shm-unlock 1)
+(defconst sqlite-shm-lock 2)
+(defconst sqlite-shm-shared 4)
+(defconst sqlite-shm-exclusive 8)
+(defconst sqlite-shm-nlock 8)
+(defconst sqlite-config-singlethread 1)
+(defconst sqlite-config-multithread 2)
+(defconst sqlite-config-serialized 3)
+(defconst sqlite-config-malloc 4)
+(defconst sqlite-config-getmalloc 5)
+(defconst sqlite-config-scratch 6)
+(defconst sqlite-config-pagecache 7)
+(defconst sqlite-config-heap 8)
+(defconst sqlite-config-memstatus 9)
+(defconst sqlite-config-mutex 10)
+(defconst sqlite-config-getmutex 11)
+(defconst sqlite-config-lookaside 13)
+(defconst sqlite-config-pcache 14)
+(defconst sqlite-config-getpcache 15)
+(defconst sqlite-config-log 16)
+(defconst sqlite-config-uri 17)
+(defconst sqlite-config-pcache2 18)
+(defconst sqlite-config-getpcache2 19)
+(defconst sqlite-config-covering-index-scan 20)
+(defconst sqlite-config-sqllog 21)
+(defconst sqlite-config-mmap-size 22)
+(defconst sqlite-config-win32-heapsize 23)
+(defconst sqlite-config-pcache-hdrsz 24)
+(defconst sqlite-config-pmasz 25)
+(defconst sqlite-config-stmtjrnl-spill 26)
+(defconst sqlite-dbconfig-maindbname 1000)
+(defconst sqlite-dbconfig-lookaside 1001)
+(defconst sqlite-dbconfig-enable-fkey 1002)
+(defconst sqlite-dbconfig-enable-trigger 1003)
+(defconst sqlite-dbconfig-enable-fts3-tokenizer 1004)
+(defconst sqlite-dbconfig-enable-load-extension 1005)
+(defconst sqlite-dbconfig-no-ckpt-on-close 1006)
+(defconst sqlite-deny 1)
+(defconst sqlite-ignore 2)
+(defconst sqlite-create-index 1)
+(defconst sqlite-create-table 2)
+(defconst sqlite-create-temp-index 3)
+(defconst sqlite-create-temp-table 4)
+(defconst sqlite-create-temp-trigger 5)
+(defconst sqlite-create-temp-view 6)
+(defconst sqlite-create-trigger 7)
+(defconst sqlite-create-view 8)
+(defconst sqlite-delete 9)
+(defconst sqlite-drop-index 10)
+(defconst sqlite-drop-table 11)
+(defconst sqlite-drop-temp-index 12)
+(defconst sqlite-drop-temp-table 13)
+(defconst sqlite-drop-temp-trigger 14)
+(defconst sqlite-drop-temp-view 15)
+(defconst sqlite-drop-trigger 16)
+(defconst sqlite-drop-view 17)
+(defconst sqlite-insert 18)
+(defconst sqlite-pragma 19)
+(defconst sqlite-read 20)
+(defconst sqlite-select 21)
+(defconst sqlite-transaction 22)
+(defconst sqlite-update 23)
+(defconst sqlite-attach 24)
+(defconst sqlite-detach 25)
+(defconst sqlite-alter-table 26)
+(defconst sqlite-reindex 27)
+(defconst sqlite-analyze 28)
+(defconst sqlite-create-vtable 29)
+(defconst sqlite-drop-vtable 30)
+(defconst sqlite-function 31)
+(defconst sqlite-savepoint 32)
+(defconst sqlite-copy 0)
+(defconst sqlite-recursive 33)
+(defconst sqlite-trace-stmt 1)
+(defconst sqlite-trace-profile 2)
+(defconst sqlite-trace-row 4)
+(defconst sqlite-trace-close 8)
+(defconst sqlite-limit-length 0)
+(defconst sqlite-limit-sql-length 1)
+(defconst sqlite-limit-column 2)
+(defconst sqlite-limit-expr-depth 3)
+(defconst sqlite-limit-compound-select 4)
+(defconst sqlite-limit-vdbe-op 5)
+(defconst sqlite-limit-function-arg 6)
+(defconst sqlite-limit-attached 7)
+(defconst sqlite-limit-like-pattern-length 8)
+(defconst sqlite-limit-variable-number 9)
+(defconst sqlite-limit-trigger-depth 10)
+(defconst sqlite-limit-worker-threads 11)
+(defconst sqlite-integer 1)
+(defconst sqlite-float 2)
+(defconst sqlite-blob 4)
+(defconst sqlite-null 5)
+(defconst sqlite3-text 3)
+(defconst sqlite-utf8 1)
+(defconst sqlite-utf16le 2)
+(defconst sqlite-utf16be 3)
+(defconst sqlite-utf16 4)
+(defconst sqlite-any 5)
+(defconst sqlite-utf16-aligned 8)
+(defconst sqlite-deterministic 2048)
+(defconst sqlite-static 0)
+(defconst sqlite-transient -1)
+(defconst sqlite-index-scan-unique 1)
+(defconst sqlite-index-constraint-eq 2)
+(defconst sqlite-index-constraint-gt 4)
+(defconst sqlite-index-constraint-le 8)
+(defconst sqlite-index-constraint-lt 16)
+(defconst sqlite-index-constraint-ge 32)
+(defconst sqlite-index-constraint-match 64)
+(defconst sqlite-index-constraint-like 65)
+(defconst sqlite-index-constraint-glob 66)
+(defconst sqlite-index-constraint-regexp 67)
+(defconst sqlite-mutex-fast 0)
+(defconst sqlite-mutex-recursive 1)
+(defconst sqlite-mutex-static-master 2)
+(defconst sqlite-mutex-static-mem 3)
+(defconst sqlite-mutex-static-mem2 4)
+(defconst sqlite-mutex-static-open 4)
+(defconst sqlite-mutex-static-prng 5)
+(defconst sqlite-mutex-static-lru 6)
+(defconst sqlite-mutex-static-lru2 7)
+(defconst sqlite-mutex-static-pmem 7)
+(defconst sqlite-mutex-static-app1 8)
+(defconst sqlite-mutex-static-app2 9)
+(defconst sqlite-mutex-static-app3 10)
+(defconst sqlite-mutex-static-vfs1 11)
+(defconst sqlite-mutex-static-vfs2 12)
+(defconst sqlite-mutex-static-vfs3 13)
+(defconst sqlite-testctrl-first 5)
+(defconst sqlite-testctrl-prng-save 5)
+(defconst sqlite-testctrl-prng-restore 6)
+(defconst sqlite-testctrl-prng-reset 7)
+(defconst sqlite-testctrl-bitvec-test 8)
+(defconst sqlite-testctrl-fault-install 9)
+(defconst sqlite-testctrl-benign-malloc-hooks 10)
+(defconst sqlite-testctrl-pending-byte 11)
+(defconst sqlite-testctrl-assert 12)
+(defconst sqlite-testctrl-always 13)
+(defconst sqlite-testctrl-reserve 14)
+(defconst sqlite-testctrl-optimizations 15)
+(defconst sqlite-testctrl-iskeyword 16)
+(defconst sqlite-testctrl-scratchmalloc 17)
+(defconst sqlite-testctrl-localtime-fault 18)
+(defconst sqlite-testctrl-explain-stmt 19)
+(defconst sqlite-testctrl-once-reset-threshold 19)
+(defconst sqlite-testctrl-never-corrupt 20)
+(defconst sqlite-testctrl-vdbe-coverage 21)
+(defconst sqlite-testctrl-byteorder 22)
+(defconst sqlite-testctrl-isinit 23)
+(defconst sqlite-testctrl-sorter-mmap 24)
+(defconst sqlite-testctrl-imposter 25)
+(defconst sqlite-testctrl-last 25)
+(defconst sqlite-status-memory-used 0)
+(defconst sqlite-status-pagecache-used 1)
+(defconst sqlite-status-pagecache-overflow 2)
+(defconst sqlite-status-scratch-used 3)
+(defconst sqlite-status-scratch-overflow 4)
+(defconst sqlite-status-malloc-size 5)
+(defconst sqlite-status-parser-stack 6)
+(defconst sqlite-status-pagecache-size 7)
+(defconst sqlite-status-scratch-size 8)
+(defconst sqlite-status-malloc-count 9)
+(defconst sqlite-dbstatus-lookaside-used 0)
+(defconst sqlite-dbstatus-cache-used 1)
+(defconst sqlite-dbstatus-schema-used 2)
+(defconst sqlite-dbstatus-stmt-used 3)
+(defconst sqlite-dbstatus-lookaside-hit 4)
+(defconst sqlite-dbstatus-lookaside-miss-size 5)
+(defconst sqlite-dbstatus-lookaside-miss-full 6)
+(defconst sqlite-dbstatus-cache-hit 7)
+(defconst sqlite-dbstatus-cache-miss 8)
+(defconst sqlite-dbstatus-cache-write 9)
+(defconst sqlite-dbstatus-deferred-fks 10)
+(defconst sqlite-dbstatus-cache-used-shared 11)
+(defconst sqlite-dbstatus-max 11)
+(defconst sqlite-stmtstatus-fullscan-step 1)
+(defconst sqlite-stmtstatus-sort 2)
+(defconst sqlite-stmtstatus-autoindex 3)
+(defconst sqlite-stmtstatus-vm-step 4)
+(defconst sqlite-checkpoint-passive 0)
+(defconst sqlite-checkpoint-full 1)
+(defconst sqlite-checkpoint-restart 2)
+(defconst sqlite-checkpoint-truncate 3)
+(defconst sqlite-vtab-constraint-support 1)
+(defconst sqlite-rollback 1)
+(defconst sqlite-fail 3)
+(defconst sqlite-replace 5)
+(defconst sqlite-scanstat-nloop 0)
+(defconst sqlite-scanstat-nvisit 1)
+(defconst sqlite-scanstat-est 2)
+(defconst sqlite-scanstat-name 3)
+(defconst sqlite-scanstat-explain 4)
+(defconst sqlite-scanstat-selectid 5)
+(provide 'sqlite3-napi)
\ No newline at end of file
diff --git a/tools/gen-consts.py b/tools/gen-consts.py
new file mode 100755
index 0000000000..752f606ff6
--- /dev/null
+++ b/tools/gen-consts.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import re
+
+c_src="""
+#include <stdio.h>
+#include <sqlite3.h>
+
+int main(int argc, char *argv[]) {
+ printf(\"(require 'sqlite3-napi-module)\\n");
+"""
+print(c_src)
+for line in sys.stdin.readlines():
+ line.rstrip()
+ fields = re.split("\s+", line, 3)
+ name = re.sub("_", "-", fields[1].lower())
+ if len(fields) > 2 and fields[2] != "":
+ #print("<{0}>-<{1}>".format(fields[1], fields[2]), file=sys.stderr)
+ if fields[2].startswith('"'):
+ print(' printf("(defconst {1} \\"%s\\")\\n", {0});'.format(fields[1],
name))
+ else:
+ print(' printf("(defconst {1} %d)\\n", {0});'.format(fields[1], name))
+print(' printf("(provide \'sqlite3-napi)");')
+print("}")
diff --git a/tools/run.sh b/tools/run.sh
new file mode 100755
index 0000000000..4f052f7044
--- /dev/null
+++ b/tools/run.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+grep "^#define SQLITE" /usr/include/sqlite3.h | ./gen-consts.py > def.c
+gcc -o def def.c -lsqlite3
+./def > ../sqlite3-napi.el
- [nongnu] branch elpa/sqlite3 created (now 50b814063b), ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 04fc60e2cf 01/62: Initial commit, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 48c913559f 03/62: -std=c99 to Makefile, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 94f0c69538 06/62: improved doc, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 ce41ac18bc 02/62: first commit,
ELPA Syncer <=
- [nongnu] elpa/sqlite3 8ad45baacf 05/62: fixed memory leak; used sqlite3_close_v2, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 ed7ea9c919 18/62: removed sqlite3-api-constants.el from repo, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 0bae137c63 26/62: LIB and INC added to Makefile, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 9e6dbe5d37 15/62: fixed typo, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 0131cc617d 19/62: python3 -> python, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 7abb3c6f70 28/62: fixed callback example; added Homebrew linkage, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 71d35506ec 07/62: minor adjustment to Makefile, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 a2d5e39133 43/62: Removed unused codes from consts.c, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 b72c2c40d9 44/62: Better handling of SQLite codes in consts.c, ELPA Syncer, 2023/03/14
- [nongnu] elpa/sqlite3 a51467b031 04/62: added test for Emacs 25.1, ELPA Syncer, 2023/03/14