bomberpac/diet-sqlite/init.fnl
Jeremy Penner fd3fcbd978 integrate sqlite, reorganize code
* diet-sqlite - a cleaned-up, slightly updated, and de-ooped version of
  https://github.com/Wiladams/LJIT2SQLite
* rename waltz -> sqlog
* separate compiler from driver
* introduce uniform syntax for actions
2022-03-29 13:24:34 -04:00

62 lines
2.3 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 :ffi))
(local util (require :lib.util))
(local {: SQLITE_OK : SQLITE_INTEGER : SQLITE_FLOAT : SQLITE_NULL : SQLITE_BLOB : SQLITE_TEXT} (require :diet-sqlite.codes))
(local sql {:step sqlffi.sqlite3_step
:reset sqlffi.sqlite3_reset})
(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 ...))
sql