aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/sqlite/sqlite.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/sqlite/sqlite.v')
-rw-r--r--v_windows/v/vlib/sqlite/sqlite.v236
1 files changed, 236 insertions, 0 deletions
diff --git a/v_windows/v/vlib/sqlite/sqlite.v b/v_windows/v/vlib/sqlite/sqlite.v
new file mode 100644
index 0000000..4bbe40e
--- /dev/null
+++ b/v_windows/v/vlib/sqlite/sqlite.v
@@ -0,0 +1,236 @@
+module sqlite
+
+$if freebsd || openbsd {
+ #flag -I/usr/local/include
+ #flag -L/usr/local/lib
+}
+$if windows {
+ #flag windows -I@VEXEROOT/thirdparty/sqlite
+ #flag windows -L@VEXEROOT/thirdparty/sqlite
+ #flag windows @VEXEROOT/thirdparty/sqlite/sqlite3.o
+} $else {
+ #flag -lsqlite3
+}
+
+#include "sqlite3.h"
+
+const (
+ sqlite_ok = 0
+ sqlite_error = 1
+ sqlite_row = 100
+ sqlite_done = 101
+)
+
+struct C.sqlite3 {
+}
+
+struct C.sqlite3_stmt {
+}
+
+struct Stmt {
+ stmt &C.sqlite3_stmt
+ db &DB
+}
+
+struct SQLError {
+ msg string
+ code int
+}
+
+//
+pub struct DB {
+pub mut:
+ is_open bool
+mut:
+ conn &C.sqlite3
+}
+
+pub fn (db DB) str() string {
+ return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
+}
+
+pub struct Row {
+pub mut:
+ vals []string
+}
+
+//
+fn C.sqlite3_open(&char, &&C.sqlite3) int
+
+fn C.sqlite3_close(&C.sqlite3) int
+
+fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64
+
+//
+fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int
+
+fn C.sqlite3_step(&C.sqlite3_stmt) int
+
+fn C.sqlite3_finalize(&C.sqlite3_stmt) int
+
+//
+fn C.sqlite3_column_name(&C.sqlite3_stmt, int) &char
+
+fn C.sqlite3_column_text(&C.sqlite3_stmt, int) &byte
+
+fn C.sqlite3_column_int(&C.sqlite3_stmt, int) int
+
+fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) i64
+
+fn C.sqlite3_column_double(&C.sqlite3_stmt, int) f64
+
+fn C.sqlite3_column_count(&C.sqlite3_stmt) int
+
+//
+fn C.sqlite3_errstr(int) &char
+
+fn C.sqlite3_errmsg(&C.sqlite3) &char
+
+fn C.sqlite3_free(voidptr)
+
+// connect Opens the connection with a database.
+pub fn connect(path string) ?DB {
+ db := &C.sqlite3(0)
+ code := C.sqlite3_open(&char(path.str), &db)
+ if code != 0 {
+ return IError(&SQLError{
+ msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
+ code: code
+ })
+ }
+ return DB{
+ conn: db
+ is_open: true
+ }
+}
+
+// close Closes the DB.
+// TODO: For all functions, determine whether the connection is
+// closed first, and determine what to do if it is
+pub fn (mut db DB) close() ?bool {
+ code := C.sqlite3_close(db.conn)
+ if code == 0 {
+ db.is_open = false
+ } else {
+ return IError(&SQLError{
+ msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
+ code: code
+ })
+ }
+ return true // successfully closed
+}
+
+// Only for V ORM
+fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
+ x := C.sqlite3_step(stmt)
+ if x != C.SQLITE_OK && x != C.SQLITE_DONE {
+ C.puts(C.sqlite3_errstr(x))
+ }
+
+ res := C.sqlite3_column_int(stmt, 0)
+ C.sqlite3_finalize(stmt)
+ return res
+}
+
+// Returns last insert rowid
+// https://www.sqlite.org/c3ref/last_insert_rowid.html
+pub fn (db DB) last_insert_rowid() i64 {
+ return C.sqlite3_last_insert_rowid(db.conn)
+}
+
+// Returns a single cell with value int.
+pub fn (db DB) q_int(query string) int {
+ stmt := &C.sqlite3_stmt(0)
+ C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
+ C.sqlite3_step(stmt)
+
+ res := C.sqlite3_column_int(stmt, 0)
+ C.sqlite3_finalize(stmt)
+ return res
+}
+
+// Returns a single cell with value string.
+pub fn (db DB) q_string(query string) string {
+ stmt := &C.sqlite3_stmt(0)
+ defer {
+ C.sqlite3_finalize(stmt)
+ }
+ C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
+ C.sqlite3_step(stmt)
+
+ val := unsafe { &byte(C.sqlite3_column_text(stmt, 0)) }
+ return if val != &byte(0) { unsafe { tos_clone(val) } } else { '' }
+}
+
+// Execute the query on db, return an array of all the results, alongside any result code.
+// Result codes: https://www.sqlite.org/rescode.html
+pub fn (db DB) exec(query string) ([]Row, int) {
+ stmt := &C.sqlite3_stmt(0)
+ C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
+ nr_cols := C.sqlite3_column_count(stmt)
+ mut res := 0
+ mut rows := []Row{}
+ for {
+ res = C.sqlite3_step(stmt)
+ // Result Code SQLITE_ROW; Another row is available
+ if res != 100 {
+ // C.puts(C.sqlite3_errstr(res))
+ break
+ }
+ mut row := Row{}
+ for i in 0 .. nr_cols {
+ val := unsafe { &byte(C.sqlite3_column_text(stmt, i)) }
+ if val == &byte(0) {
+ row.vals << ''
+ } else {
+ row.vals << unsafe { tos_clone(val) }
+ }
+ }
+ rows << row
+ }
+ C.sqlite3_finalize(stmt)
+ return rows, res
+}
+
+// Execute a query, handle error code
+// Return the first row from the resulting table
+pub fn (db DB) exec_one(query string) ?Row {
+ rows, code := db.exec(query)
+ if rows.len == 0 {
+ return IError(&SQLError{
+ msg: 'No rows'
+ code: code
+ })
+ } else if code != 101 {
+ return IError(&SQLError{
+ msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
+ code: code
+ })
+ }
+ return rows[0]
+}
+
+pub fn (db DB) error_message(code int, query string) IError {
+ msg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
+ return IError(&SQLError{
+ msg: '$msg ($code) ($query)'
+ code: code
+ })
+}
+
+// In case you don't expect any result, but still want an error code
+// e.g. INSERT INTO ... VALUES (...)
+pub fn (db DB) exec_none(query string) int {
+ _, code := db.exec(query)
+ return code
+}
+
+/*
+TODO
+pub fn (db DB) exec_param(query string, param string) []Row {
+}
+*/
+
+pub fn (db DB) create_table(table_name string, columns []string) {
+ db.exec('create table if not exists $table_name (' + columns.join(',\n') + ')')
+}