Add sqlite wrapper module (:std/db/sqlite)

ober

ae945bac49d7084fbbc2a494f7f57e8a19bf58ba

diff --git a/Makefile b/Makefile
index 7b53799..987d0e5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
 SCHEME = scheme
 LIBDIRS = lib
 # External chez-* library paths for wrapper modules
-CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src:$(HOME)/mine/chez-inotify/src:$(HOME)/mine/chez-crypto/src
+CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src:$(HOME)/mine/chez-inotify/src:$(HOME)/mine/chez-crypto/src:$(HOME)/mine/chez-sqlite/src
 # Shared object paths for FFI-based chez-* libraries
-CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll:$(HOME)/mine/chez-inotify:$(HOME)/mine/chez-crypto
+CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll:$(HOME)/mine/chez-inotify:$(HOME)/mine/chez-crypto:$(HOME)/mine/chez-sqlite
 
 .PHONY: test test-reader test-core test-runtime test-stdlib test-ffi test-modules test-expanded test-wrappers clean
 
@@ -66,6 +66,9 @@ test-wrappers:
 	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
 		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-crypto.ss 2>/dev/null \
 		|| echo "  crypto: SKIP (requires chez_crypto_shim.so)"
+	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
+		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-sqlite.ss 2>/dev/null \
+		|| echo "  sqlite: SKIP (requires chez_sqlite_shim.so)"
 
 test-all: test test-wrappers
 
diff --git a/lib/std/db/sqlite.sls b/lib/std/db/sqlite.sls
new file mode 100644
index 0000000..66321cd
--- /dev/null
+++ b/lib/std/db/sqlite.sls
@@ -0,0 +1,26 @@
+#!chezscheme
+;;; :std/db/sqlite -- SQLite3 database (wraps chez-sqlite)
+;;; Requires: chez_sqlite_shim.so, libsqlite3.so
+
+(library (std db sqlite)
+  (export
+    ;; Core
+    sqlite-open sqlite-close
+    sqlite-exec sqlite-eval sqlite-query
+    ;; Prepared statements
+    sqlite-prepare sqlite-finalize
+    sqlite-step sqlite-reset sqlite-clear-bindings
+    ;; Binding
+    sqlite-bind! sqlite-bind-null!
+    ;; Column access
+    sqlite-column-count sqlite-column-name sqlite-column-type
+    sqlite-column-value sqlite-columns
+    ;; Metadata
+    sqlite-last-insert-rowid sqlite-changes sqlite-errmsg
+    ;; Constants
+    SQLITE_ROW SQLITE_DONE SQLITE_OK
+    SQLITE_INTEGER SQLITE_FLOAT SQLITE_TEXT SQLITE_BLOB SQLITE_NULL)
+
+  (import (chez-sqlite))
+
+  ) ;; end library
diff --git a/tests/test-wrapper-sqlite.ss b/tests/test-wrapper-sqlite.ss
new file mode 100644
index 0000000..5b555d8
--- /dev/null
+++ b/tests/test-wrapper-sqlite.ss
@@ -0,0 +1,45 @@
+#!chezscheme
+(import (chezscheme) (std db sqlite))
+
+(define pass 0)
+(define fail 0)
+(define-syntax chk
+  (syntax-rules (=>)
+    [(_ expr => expected)
+     (let ([r expr] [e expected])
+       (if (equal? r e)
+         (set! pass (+ pass 1))
+         (begin (set! fail (+ fail 1))
+                (display "FAIL: ") (write 'expr)
+                (display " => ") (write r)
+                (display " expected ") (write e) (newline))))]))
+
+(define test-db "/tmp/jerboa-sqlite-test.db")
+(when (file-exists? test-db) (delete-file test-db))
+
+;; Open
+(define db (sqlite-open test-db))
+(chk (not (zero? db)) => #t)
+
+;; Create + insert
+(sqlite-exec db "CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)")
+(sqlite-eval db "INSERT INTO t (val) VALUES (?)" "hello")
+(sqlite-eval db "INSERT INTO t (val) VALUES (?)" "world")
+(chk (sqlite-last-insert-rowid db) => 2)
+(chk (sqlite-changes db) => 1)
+
+;; Query
+(let ([rows (sqlite-query db "SELECT id, val FROM t ORDER BY id")])
+  (chk (length rows) => 2)
+  (chk (vector-ref (car rows) 1) => "hello")
+  (chk (vector-ref (cadr rows) 1) => "world"))
+
+;; Cleanup
+(sqlite-close db)
+(delete-file test-db)
+
+(newline)
+(display "sqlite wrapper: ")
+(display pass) (display " passed, ")
+(display fail) (display " failed") (newline)
+(when (> fail 0) (exit 1))