1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/sqlite/sqlite.v

343 lines
8.4 KiB
V
Raw Normal View History

2019-10-27 23:32:15 +03:00
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
}
2019-10-27 23:32:15 +03:00
#include "sqlite3.h"
// https://www.sqlite.org/rescode.html
2021-11-17 05:45:50 +03:00
pub const (
sqlite_ok = 0
sqlite_error = 1
sqlite_row = 100
sqlite_done = 101
sqlite_cantopen = 14
sqlite_ioerr_read = 266
sqlite_ioerr_short_read = 522
sqlite_ioerr_write = 778
sqlite_ioerr_fsync = 1034
sqlite_ioerr_fstat = 1802
sqlite_ioerr_delete = 2570
sqlite_open_main_db = 0x00000100
sqlite_open_temp_db = 0x00000200
sqlite_open_transient_db = 0x00000400
sqlite_open_main_journal = 0x00000800
sqlite_open_temp_journal = 0x00001000
sqlite_open_subjournal = 0x00002000
sqlite_open_super_journal = 0x00004000
sqlite_open_wal = 0x00080000
)
pub enum SyncMode {
off
normal
full
}
pub enum JournalMode {
off
delete
truncate
persist
memory
}
pub struct C.sqlite3 {
2020-10-19 21:11:04 +03:00
}
pub struct C.sqlite3_stmt {
2020-10-19 21:11:04 +03:00
}
2022-08-31 14:43:20 +03:00
[heap]
pub struct Stmt {
stmt &C.sqlite3_stmt = unsafe { nil }
db &DB = unsafe { nil }
}
pub struct SQLError {
MessageError
}
//
2022-08-31 14:43:20 +03:00
[heap]
pub struct DB {
pub mut:
is_open bool
2019-10-27 23:32:15 +03:00
mut:
conn &C.sqlite3 = unsafe { nil }
2019-10-27 23:32:15 +03:00
}
2022-08-31 14:43:20 +03:00
pub fn (db &DB) str() string {
return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
}
pub struct Row {
2019-10-27 23:32:15 +03:00
pub mut:
vals []string
}
2020-10-19 21:11:04 +03:00
//
2021-04-06 22:15:14 +03:00
fn C.sqlite3_open(&char, &&C.sqlite3) int
2020-10-19 21:11:04 +03:00
fn C.sqlite3_close(&C.sqlite3) int
2020-10-19 21:11:04 +03:00
2022-01-06 12:47:20 +03:00
fn C.sqlite3_busy_timeout(db &C.sqlite3, ms int) int
fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64
//
2021-04-06 22:15:14 +03:00
fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int
2020-10-19 21:11:04 +03:00
fn C.sqlite3_step(&C.sqlite3_stmt) int
2020-10-19 21:11:04 +03:00
fn C.sqlite3_finalize(&C.sqlite3_stmt) int
2020-10-19 21:11:04 +03:00
//
2021-04-06 22:15:14 +03:00
fn C.sqlite3_column_name(&C.sqlite3_stmt, int) &char
2020-10-19 21:11:04 +03:00
2022-04-15 18:25:45 +03:00
fn C.sqlite3_column_text(&C.sqlite3_stmt, int) &u8
2020-10-19 21:11:04 +03:00
fn C.sqlite3_column_int(&C.sqlite3_stmt, int) int
2020-10-19 21:11:04 +03:00
fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) i64
2020-10-19 21:11:04 +03:00
fn C.sqlite3_column_double(&C.sqlite3_stmt, int) f64
2020-10-19 21:11:04 +03:00
fn C.sqlite3_column_count(&C.sqlite3_stmt) int
2020-10-19 21:11:04 +03:00
//
2021-04-06 22:15:14 +03:00
fn C.sqlite3_errstr(int) &char
2020-10-19 21:11:04 +03:00
fn C.sqlite3_errmsg(&C.sqlite3) &char
fn C.sqlite3_free(voidptr)
2022-11-14 17:23:42 +03:00
fn C.sqlite3_changes(&C.sqlite3) int
// connect Opens the connection with a database.
pub fn connect(path string) !DB {
db := &C.sqlite3(unsafe { nil })
code := C.sqlite3_open(&char(path.str), &db)
if code != 0 {
2022-10-28 19:08:30 +03:00
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
code: code
2022-10-28 19:08:30 +03:00
}
2020-06-16 13:14:22 +03:00
}
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 {
2022-10-28 19:08:30 +03:00
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
code: code
2022-10-28 19:08:30 +03:00
}
}
return true // successfully closed
2019-10-27 23:32:15 +03:00
}
2020-06-17 01:59:33 +03:00
// 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 {
2020-07-16 01:48:10 +03:00
C.puts(C.sqlite3_errstr(x))
}
2020-06-17 01:59:33 +03:00
res := C.sqlite3_column_int(stmt, 0)
C.sqlite3_finalize(stmt)
return res
}
2022-11-14 17:23:42 +03:00
// last_insert_rowid returns last inserted rowid
// https://www.sqlite.org/c3ref/last_insert_rowid.html
2022-08-31 14:43:20 +03:00
pub fn (db &DB) last_insert_rowid() i64 {
return C.sqlite3_last_insert_rowid(db.conn)
}
2022-11-14 17:23:42 +03:00
// get_affected_rows_count returns `sqlite changes()` meaning amount of rows affected by most recent sql query
pub fn (db &DB) get_affected_rows_count() int {
return C.sqlite3_changes(db.conn)
}
// Returns a single cell with value int.
2022-08-31 14:43:20 +03:00
pub fn (db &DB) q_int(query string) int {
stmt := &C.sqlite3_stmt(unsafe { nil })
2022-08-31 14:43:20 +03:00
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
2019-10-27 23:32:15 +03:00
C.sqlite3_step(stmt)
2019-10-27 23:32:15 +03:00
res := C.sqlite3_column_int(stmt, 0)
return res
}
// Returns a single cell with value string.
2022-08-31 14:43:20 +03:00
pub fn (db &DB) q_string(query string) string {
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
2019-10-27 23:32:15 +03:00
C.sqlite3_step(stmt)
2022-04-15 14:58:56 +03:00
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
return if val != &u8(0) { unsafe { tos_clone(val) } } else { '' }
2019-10-27 23:32:15 +03:00
}
// Execute the query on db, return an array of all the results, alongside any result code.
// Result codes: https://www.sqlite.org/rescode.html
2022-08-31 14:43:20 +03:00
[manualfree]
pub fn (db &DB) exec(query string) ([]Row, int) {
stmt := &C.sqlite3_stmt(unsafe { nil })
2022-08-31 14:43:20 +03:00
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
2019-10-27 23:32:15 +03:00
nr_cols := C.sqlite3_column_count(stmt)
mut res := 0
2020-04-26 17:25:54 +03:00
mut rows := []Row{}
2019-10-27 23:32:15 +03:00
for {
res = C.sqlite3_step(stmt)
2020-04-26 17:25:54 +03:00
// Result Code SQLITE_ROW; Another row is available
if res != 100 {
2020-10-19 21:11:04 +03:00
// C.puts(C.sqlite3_errstr(res))
2019-10-27 23:32:15 +03:00
break
2019-12-04 13:08:28 +03:00
}
2019-10-27 23:32:15 +03:00
mut row := Row{}
for i in 0 .. nr_cols {
2022-04-15 14:58:56 +03:00
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
if val == &u8(0) {
row.vals << ''
} else {
row.vals << unsafe { tos_clone(val) }
}
2019-10-27 23:32:15 +03:00
}
rows << row
}
2020-10-19 21:11:04 +03:00
return rows, res
2019-10-27 23:32:15 +03:00
}
// Execute a query, handle error code
// Return the first row from the resulting table
2022-08-31 14:43:20 +03:00
[manualfree]
pub fn (db &DB) exec_one(query string) !Row {
2020-10-19 21:11:04 +03:00
rows, code := db.exec(query)
2022-08-31 14:43:20 +03:00
defer {
unsafe { rows.free() }
}
if rows.len == 0 {
2022-10-28 19:08:30 +03:00
return &SQLError{
msg: 'No rows'
code: code
2022-10-28 19:08:30 +03:00
}
} else if code != 101 {
2022-10-28 19:08:30 +03:00
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
code: code
2022-10-28 19:08:30 +03:00
}
}
2022-08-31 14:43:20 +03:00
res := rows[0]
return res
2019-10-27 23:32:15 +03:00
}
2022-08-31 14:43:20 +03:00
[manualfree]
pub fn (db &DB) error_message(code int, query string) IError {
errmsg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
msg := '${errmsg} (${code}) (${query})'
2022-08-31 14:43:20 +03:00
unsafe { errmsg.free() }
return SQLError{
msg: msg
code: code
2022-08-31 14:43:20 +03:00
}
}
// Execute a query returning only the result code.
// In case you don't expect any row results, but still want a result code.
// e.g. INSERT INTO ... VALUES (...)
2022-08-31 14:43:20 +03:00
pub fn (db &DB) exec_none(query string) int {
stmt := &C.sqlite3_stmt(unsafe { nil })
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
code := C.sqlite3_step(stmt)
C.sqlite3_finalize(stmt)
return code
}
2020-10-19 21:11:04 +03:00
/*
TODO
2022-08-31 14:43:20 +03:00
pub fn (db &DB) exec_param(query string, param string) []Row {
2019-10-27 23:32:15 +03:00
}
*/
2021-01-20 11:30:26 +03:00
// Issue a "create table if not exists" command to the db.
// Creates table named 'table_name', with columns generated from 'columns' array.
// Default columns type will be TEXT.
2022-08-31 14:43:20 +03:00
pub fn (db &DB) create_table(table_name string, columns []string) {
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')
2021-01-20 11:30:26 +03:00
}
2022-01-06 12:47:20 +03:00
// Set a busy timeout in milliseconds.
// Sleeps for a specified amount of time when a table is locked. The handler
// will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
// (see https://www.sqlite.org/c3ref/busy_timeout.html)
2022-08-31 14:43:20 +03:00
pub fn (db &DB) busy_timeout(ms int) int {
2022-01-06 12:47:20 +03:00
return C.sqlite3_busy_timeout(db.conn, ms)
}
// Sets disk synchronization mode,
// which controls how aggressively SQLite will write data to physical storage.
// off: No syncs at all. (fastest)
// normal: Sync after each sequence of critical disk operations.
// full: Sync after each critical disk operation (slowest).
2022-08-31 14:43:20 +03:00
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
if sync_mode == .off {
db.exec('pragma synchronous = OFF;')
} else if sync_mode == .full {
db.exec('pragma synchronous = FULL;')
} else {
db.exec('pragma synchronous = NORMAL;')
}
}
// Controls how the journal file is stored and processed.
// off: No journal record is kept. (fastest)
// memory: Journal record is held in memory, rather than on disk.
// delete: At the conclusion of a transaction, journal file is deleted.
// truncate: Journal file is truncated to a length of zero bytes.
// persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
2022-08-31 14:43:20 +03:00
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
if journal_mode == .off {
db.exec('pragma journal_mode = OFF;')
} else if journal_mode == .delete {
db.exec('pragma journal_mode = DELETE;')
} else if journal_mode == .truncate {
db.exec('pragma journal_mode = TRUNCATE;')
} else if journal_mode == .persist {
db.exec('pragma journal_mode = PERSIST;')
} else if journal_mode == .memory {
db.exec('pragma journal_mode = MEMORY;')
} else {
db.exec('pragma journal_mode = MEMORY;')
}
}