emacs-elpa-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]