12345678module storage910#flag -I@VMODROOT/vendor/sqlite11#flag @VMODROOT/vendor/sqlite/sqlite3.c12#include "sqlite3.h"131415pub struct C.sqlite3 {}1617pub struct C.sqlite3_stmt {}1819fn C.sqlite3_libversion() &char2021fn C.sqlite3_open_v2(filename &char, pp_db &&C.sqlite3, flags i32, vfs &char) i322223fn C.sqlite3_close_v2(db &C.sqlite3) i322425fn C.sqlite3_errmsg(db &C.sqlite3) &char2627fn C.sqlite3_busy_timeout(db &C.sqlite3, ms i32) i322829fn C.sqlite3_exec(db &C.sqlite3, query &char, callback voidptr, arg voidptr, errmsg &&char) i323031fn C.sqlite3_free(ptr voidptr)3233fn C.sqlite3_prepare_v2(db &C.sqlite3, query &char, n i32, stmt &&C.sqlite3_stmt, tail &&char) i323435fn C.sqlite3_step(stmt &C.sqlite3_stmt) i323637fn C.sqlite3_reset(stmt &C.sqlite3_stmt) i323839fn C.sqlite3_finalize(stmt &C.sqlite3_stmt) i324041fn C.sqlite3_bind_blob(stmt &C.sqlite3_stmt, idx i32, val voidptr, n i32, done voidptr) i324243fn C.sqlite3_bind_text(stmt &C.sqlite3_stmt, idx i32, val &char, n i32, done voidptr) i324445fn C.sqlite3_bind_int64(stmt &C.sqlite3_stmt, idx i32, val i64) i324647fn C.sqlite3_bind_null(stmt &C.sqlite3_stmt, idx i32) i324849fn C.sqlite3_column_blob(stmt &C.sqlite3_stmt, idx i32) voidptr5051fn C.sqlite3_column_bytes(stmt &C.sqlite3_stmt, idx i32) i325253fn C.sqlite3_column_text(stmt &C.sqlite3_stmt, idx i32) &u85455fn C.sqlite3_column_int64(stmt &C.sqlite3_stmt, idx i32) i645657fn C.sqlite3_column_type(stmt &C.sqlite3_stmt, idx i32) i325859fn C.sqlite3_changes(db &C.sqlite3) i326061const sqlite_ok = 062const sqlite_busy = 563const sqlite_row = 10064const sqlite_done = 10165const sqlite_open_readwrite = 0x0000000266const sqlite_open_create = 0x0000000467const sqlite_null = 5686970const transient = unsafe { voidptr(isize(-1)) }7172fn cstring(s string) &char {73 buf := unsafe { malloc_noscan(s.len + 1) }74 unsafe {75 vmemcpy(buf, s.str, s.len)76 buf[s.len] = 077 }78 return &char(buf)79}8081pub fn libversion() string {82 return unsafe { cstring_to_vstring(C.sqlite3_libversion()) }83}8485@[heap]86pub struct Db {87mut:88 conn &C.sqlite3 = unsafe { nil }89}90919293pub fn connect(path string, busy_timeout_ms int) !Db {94 path_c := cstring(path)95 defer {96 unsafe { free(path_c) }97 }98 mut conn := &C.sqlite3(unsafe { nil })99 flags := sqlite_open_readwrite | sqlite_open_create100 code := C.sqlite3_open_v2(path_c, &conn, flags, unsafe { nil })101 if code != sqlite_ok {102 msg := unsafe { cstring_to_vstring(C.sqlite3_errmsg(conn)) }103 C.sqlite3_close_v2(conn)104 return error('cannot open ${path}: ${msg}')105 }106 C.sqlite3_busy_timeout(conn, busy_timeout_ms)107 return Db{108 conn: conn109 }110}111112pub fn (db &Db) close() {113 if db.conn != unsafe { nil } {114 C.sqlite3_close_v2(db.conn)115 }116}117118pub fn (db &Db) last_error() string {119 return unsafe { cstring_to_vstring(C.sqlite3_errmsg(db.conn)) }120}121122pub fn (db &Db) changes() int {123 return C.sqlite3_changes(db.conn)124}125126127pub fn (db &Db) exec(query string) ! {128 query_c := cstring(query)129 defer {130 unsafe { free(query_c) }131 }132 mut errmsg := &char(unsafe { nil })133 code := C.sqlite3_exec(db.conn, query_c, unsafe { nil }, unsafe { nil }, &errmsg)134 if code == sqlite_busy {135 if errmsg != unsafe { nil } {136 C.sqlite3_free(errmsg)137 }138 return error(busy)139 }140 if code != sqlite_ok {141 msg := if errmsg != unsafe { nil } {142 unsafe { cstring_to_vstring(errmsg) }143 } else {144 db.last_error()145 }146 if errmsg != unsafe { nil } {147 C.sqlite3_free(errmsg)148 }149 return error(msg)150 }151}152153154155@[heap]156pub struct Stmt {157mut:158 stmt &C.sqlite3_stmt = unsafe { nil }159 db &Db = unsafe { nil }160}161162163pub fn (db &Db) prepare(query string) !Stmt {164 query_c := cstring(query)165 defer {166 unsafe { free(query_c) }167 }168 mut stmt := &C.sqlite3_stmt(unsafe { nil })169 code := C.sqlite3_prepare_v2(db.conn, query_c, query.len, &stmt, unsafe { nil })170 if code != sqlite_ok {171 return error('${db.last_error()} in: ${query}')172 }173 return Stmt{174 stmt: stmt175 db: db176 }177}178179pub fn (stmt &Stmt) bind_text(idx int, val string) ! {180 if C.sqlite3_bind_text(stmt.stmt, idx, val.str, val.len, transient) != sqlite_ok {181 return error(stmt.db.last_error())182 }183}184185pub fn (stmt &Stmt) bind_blob(idx int, val []u8) ! {186 mut ptr := unsafe { nil }187 if val.len > 0 {188 ptr = val.data189 }190 if C.sqlite3_bind_blob(stmt.stmt, idx, ptr, val.len, transient) != sqlite_ok {191 return error(stmt.db.last_error())192 }193}194195pub fn (stmt &Stmt) bind_int(idx int, val i64) ! {196 if C.sqlite3_bind_int64(stmt.stmt, idx, val) != sqlite_ok {197 return error(stmt.db.last_error())198 }199}200201pub fn (stmt &Stmt) bind_null(idx int) ! {202 if C.sqlite3_bind_null(stmt.stmt, idx) != sqlite_ok {203 return error(stmt.db.last_error())204 }205}206207208209210pub fn (stmt &Stmt) step() !bool {211 code := C.sqlite3_step(stmt.stmt)212 match code {213 sqlite_row { return true }214 sqlite_done { return false }215 sqlite_busy { return error(busy) }216 else { return error(stmt.db.last_error()) }217 }218}219220pub fn (stmt &Stmt) reset() {221 C.sqlite3_reset(stmt.stmt)222}223224pub fn (stmt &Stmt) finalize() {225 if stmt.stmt != unsafe { nil } {226 C.sqlite3_finalize(stmt.stmt)227 }228}229230fn (stmt &Stmt) is_null(idx int) bool {231 return C.sqlite3_column_type(stmt.stmt, idx) == sqlite_null232}233234235236237pub fn (stmt &Stmt) text(idx int) string {238 ptr := C.sqlite3_column_text(stmt.stmt, idx)239 if ptr == unsafe { nil } {240 return ''241 }242 n := C.sqlite3_column_bytes(stmt.stmt, idx)243 if n == 0 {244 return ''245 }246 mut out := []u8{len: n}247 unsafe { vmemcpy(out.data, ptr, n) }248 return out.bytestr()249}250251252pub fn (stmt &Stmt) blob(idx int) []u8 {253 ptr := C.sqlite3_column_blob(stmt.stmt, idx)254 n := C.sqlite3_column_bytes(stmt.stmt, idx)255 if ptr == unsafe { nil } || n == 0 {256 return []257 }258 mut out := []u8{len: n}259 unsafe { vmemcpy(out.data, ptr, n) }260 return out261}262263pub fn (stmt &Stmt) int(idx int) i64 {264 return C.sqlite3_column_int64(stmt.stmt, idx)265}266267pub fn (stmt &Stmt) text_or_none(idx int) ?string {268 if stmt.is_null(idx) {269 return none270 }271 return stmt.text(idx)272}273274pub fn (stmt &Stmt) int_or_none(idx int) ?i64 {275 if stmt.is_null(idx) {276 return none277 }278 return stmt.int(idx)279}