edtris/diet-sqlite/init.fnl

68 lines
2.7 KiB
Fennel

; This small library is based on https://github.com/Wiladams/LJIT2SQLite
; It is meant to be an _extremely_ thin wrapper around the SQLite API.
(local sqlffi (require :diet-sqlite.sqlite3_ffi))
(local ffi (require :diet-sqlite.ffi))
(local util (require :lib.util))
(local {: SQLITE_OK : SQLITE_INTEGER : SQLITE_FLOAT : SQLITE_NULL : SQLITE_BLOB : SQLITE_TEXT : SQLITE_TRANSIENT
: SQLITE_CONFIG_LOG : log_callback_type} (require :diet-sqlite.codes))
(local sql {:step sqlffi.sqlite3_step
:reset sqlffi.sqlite3_reset
:shutdown sqlffi.sqlite3_shutdown})
(fn sql.open [dbname]
(let [lpdb (ffi.new "sqlite3*[1]")
rc (sqlffi.sqlite3_open dbname lpdb)
db (. lpdb 0)]
; (when (not= db nil) (ffi.gc db sqlffi.sqlite3_close_v2))
(values rc (. lpdb 0))))
(fn sql.close [db]
(ffi.gc db nil)
(sqlffi.sqlite3_close_v2 db))
(fn sql.prepare [db sql ?flags]
(let [ppStmt (ffi.new "sqlite3_stmt *[1]")
rc (sqlffi.sqlite3_prepare_v3 db sql -1 (or ?flags 0) ppStmt nil)
stmt (. ppStmt 0)]
(when (not= stmt nil) (ffi.gc stmt sqlffi.sqlite3_finalize))
(values rc stmt)))
; this exists just to have a unique object identity you can shove into a table
(set sql.null {})
(fn sql.bind [stmt i ?val]
(match (type i)
:table (each [ival val (ipairs i)] (sql.assert (sql.bind stmt ival val)))
:number
(match (type ?val)
:string (sqlffi.sqlite3_bind_text stmt i ?val (length ?val) SQLITE_TRANSIENT)
:number (sqlffi.sqlite3_bind_double stmt i ?val)
:nil (sqlffi.sqlite3_bind_null stmt i)
(where :table (= ?val sql.null)) (sqlffi.sqlite3_bind_null stmt i)
_ (error (.. "Don't know how to bind value " (fv ?val))))))
(fn sql.column [stmt icol]
(let [i (- icol 1)] ; column indexes are 0-based
(match (sqlffi.sqlite3_column_type stmt i)
SQLITE_INTEGER (sqlffi.sqlite3_column_double stmt i)
SQLITE_FLOAT (sqlffi.sqlite3_column_double stmt i)
SQLITE_NULL nil
SQLITE_TEXT (ffi.string (sqlffi.sqlite3_column_text stmt i))
SQLITE_BLOB (error "no blob support right now")
?unknown (error (.. "unrecognized type " ?unknown)))))
(fn sql.columns [stmt]
(icollect [i (util.countiter (sqlffi.sqlite3_column_count stmt))] (or (sql.column stmt i) sql.null)))
(fn sql.assert-rc [rc-expect rc ...]
(if (= rc-expect rc) ... (error (.. "Expected " rc-expect ", got " rc))))
(fn sql.assert [...] (sql.assert-rc SQLITE_OK ...))
(fn sql.register-logger [logfn]
(let [callback (ffi.cast log_callback_type (fn [pArg iErrCode zMsg] (logfn (ffi.string zMsg) iErrCode)))]
(sql.assert (sqlffi.sqlite3_config SQLITE_CONFIG_LOG callback (ffi.cast "void *" nil)))))
sql