Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,672 @@
|
||||
module sqlite
|
||||
|
||||
$if freebsd || openbsd {
|
||||
#flag -I/usr/local/include
|
||||
#flag -L/usr/local/lib
|
||||
}
|
||||
$if tinyc {
|
||||
#flag -DSQLITE_DISABLE_INTRINSIC
|
||||
}
|
||||
$if $pkgconfig('sqlite3') {
|
||||
#pkgconfig sqlite3
|
||||
#include "sqlite3.h" # The SQLite header file is missing. Please install the corresponding development package.
|
||||
} $else $if windows {
|
||||
#flag -I@VEXEROOT/thirdparty/sqlite
|
||||
#flag @VEXEROOT/thirdparty/sqlite/sqlite3.o
|
||||
#include "sqlite3.h" # The SQLite header file is missing. Please run vlib/db/sqlite/install_thirdparty_sqlite.vsh to download an SQLite amalgamation.
|
||||
} $else $if darwin {
|
||||
// macOS ships libsqlite3, so do not require a separately downloaded amalgamation.
|
||||
#flag darwin -lsqlite3
|
||||
} $else {
|
||||
#flag -I@VEXEROOT/thirdparty/sqlite
|
||||
#include "sqlite3.h" # The SQLite header file is missing. Please run vlib/db/sqlite/install_thirdparty_sqlite.vsh to download an SQLite amalgamation.
|
||||
#flag @VEXEROOT/thirdparty/sqlite/sqlite3.c
|
||||
}
|
||||
$if linux {
|
||||
#flag -lm
|
||||
}
|
||||
|
||||
// https://www.sqlite.org/rescode.html
|
||||
pub const sqlite_ok = 0
|
||||
pub const sqlite_error = 1
|
||||
pub const sqlite_row = 100
|
||||
pub const sqlite_done = 101
|
||||
pub const sqlite_cantopen = 14
|
||||
pub const sqlite_ioerr_read = 266
|
||||
pub const sqlite_ioerr_short_read = 522
|
||||
pub const sqlite_ioerr_write = 778
|
||||
pub const sqlite_ioerr_fsync = 1034
|
||||
pub const sqlite_ioerr_fstat = 1802
|
||||
pub const sqlite_ioerr_delete = 2570
|
||||
|
||||
pub const sqlite_open_main_db = 0x00000100
|
||||
pub const sqlite_open_temp_db = 0x00000200
|
||||
pub const sqlite_open_transient_db = 0x00000400
|
||||
pub const sqlite_open_main_journal = 0x00000800
|
||||
pub const sqlite_open_temp_journal = 0x00001000
|
||||
pub const sqlite_open_subjournal = 0x00002000
|
||||
pub const sqlite_open_super_journal = 0x00004000
|
||||
pub const sqlite_open_wal = 0x00080000
|
||||
|
||||
pub enum SyncMode {
|
||||
off
|
||||
normal
|
||||
full
|
||||
}
|
||||
|
||||
pub enum Sqlite3TransactionLevel {
|
||||
deferred
|
||||
immediate
|
||||
exclusive
|
||||
}
|
||||
|
||||
pub enum JournalMode {
|
||||
off
|
||||
delete
|
||||
truncate
|
||||
persist
|
||||
memory
|
||||
}
|
||||
|
||||
pub struct C.sqlite3 {
|
||||
}
|
||||
|
||||
pub struct C.sqlite3_stmt {
|
||||
}
|
||||
|
||||
@[heap]
|
||||
pub struct Stmt {
|
||||
stmt &C.sqlite3_stmt = unsafe { nil }
|
||||
db &DB = unsafe { nil }
|
||||
}
|
||||
|
||||
struct SQLError {
|
||||
MessageError
|
||||
}
|
||||
|
||||
//
|
||||
@[heap]
|
||||
pub struct DB {
|
||||
pub mut:
|
||||
is_open bool
|
||||
mut:
|
||||
conn &C.sqlite3 = unsafe { nil }
|
||||
}
|
||||
|
||||
// str returns a text representation of the DB
|
||||
pub fn (db &DB) str() string {
|
||||
return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
|
||||
}
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
vals []string
|
||||
names []string
|
||||
}
|
||||
|
||||
// val returns the value at `index`.
|
||||
pub fn (row Row) val(index int) string {
|
||||
return row.vals[index]
|
||||
}
|
||||
|
||||
// values returns all row values.
|
||||
pub fn (row Row) values() []string {
|
||||
return row.vals.clone()
|
||||
}
|
||||
|
||||
// get_string returns the value for the given column name, or '' if the column is not found
|
||||
// or if the corresponding value index is out of range.
|
||||
pub fn (r &Row) get_string(col_name string) string {
|
||||
for i, name in r.names {
|
||||
if name == col_name {
|
||||
if i < r.vals.len {
|
||||
return r.vals[i]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// get_int returns the integer value for the given column name, or 0 if the column is not found.
|
||||
pub fn (r &Row) get_int(col_name string) int {
|
||||
return r.get_string(col_name).int()
|
||||
}
|
||||
|
||||
pub type Params = []string | [][]string
|
||||
|
||||
fn sqlite_cstring(s string) &char {
|
||||
cstr := unsafe { malloc_noscan(s.len + 1) }
|
||||
unsafe {
|
||||
if s.len > 0 {
|
||||
vmemcpy(cstr, s.str, s.len)
|
||||
}
|
||||
cstr[s.len] = 0
|
||||
}
|
||||
return &char(cstr)
|
||||
}
|
||||
|
||||
//
|
||||
fn C.sqlite3_open(&char, &&C.sqlite3) i32
|
||||
|
||||
fn C.sqlite3_close(&C.sqlite3) i32
|
||||
|
||||
fn C.sqlite3_busy_timeout(db &C.sqlite3, ms i32) i32
|
||||
|
||||
fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64
|
||||
|
||||
//
|
||||
fn C.sqlite3_prepare_v2(&C.sqlite3, &char, i32, &&C.sqlite3_stmt, &&char) i32
|
||||
|
||||
fn C.sqlite3_step(&C.sqlite3_stmt) i32
|
||||
|
||||
fn C.sqlite3_reset(&C.sqlite3_stmt) i32
|
||||
|
||||
fn C.sqlite3_finalize(&C.sqlite3_stmt) i32
|
||||
|
||||
//
|
||||
fn C.sqlite3_column_name(&C.sqlite3_stmt, i32) &char
|
||||
|
||||
fn C.sqlite3_column_text(&C.sqlite3_stmt, i32) &u8
|
||||
|
||||
fn C.sqlite3_column_int(&C.sqlite3_stmt, i32) i32
|
||||
|
||||
fn C.sqlite3_column_int64(&C.sqlite3_stmt, i32) i64
|
||||
|
||||
fn C.sqlite3_column_double(&C.sqlite3_stmt, i32) f64
|
||||
|
||||
fn C.sqlite3_column_count(&C.sqlite3_stmt) i32
|
||||
|
||||
fn C.sqlite3_column_type(&C.sqlite3_stmt, i32) i32
|
||||
|
||||
fn C.sqlite3_column_bytes(&C.sqlite3_stmt, i32) i32
|
||||
|
||||
//
|
||||
fn C.sqlite3_errstr(i32) &char
|
||||
|
||||
fn C.sqlite3_errmsg(&C.sqlite3) &char
|
||||
|
||||
fn C.sqlite3_free(voidptr)
|
||||
|
||||
fn C.sqlite3_changes(&C.sqlite3) i32
|
||||
|
||||
// connect Opens the connection with a database.
|
||||
pub fn connect(path string) !DB {
|
||||
db := &C.sqlite3(unsafe { nil })
|
||||
path_cstr := sqlite_cstring(path)
|
||||
defer {
|
||||
unsafe { free(path_cstr) }
|
||||
}
|
||||
code := C.sqlite3_open(path_cstr, &db)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db))) }
|
||||
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() ! {
|
||||
code := C.sqlite3_close(db.conn)
|
||||
if code == 0 {
|
||||
db.is_open = false
|
||||
} else {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// last_insert_rowid returns last inserted 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// q_int returns a single integer value, from the first column of the result of executing `query`, or an error on failure
|
||||
pub fn (db &DB) q_int(query string) !int {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> q_int query: "${query}"')
|
||||
}
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if pres != sqlite_ok {
|
||||
return db.error_message(pres, query)
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
code := C.sqlite3_step(stmt)
|
||||
if code != sqlite_row {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
res := C.sqlite3_column_int(stmt, 0)
|
||||
return res
|
||||
}
|
||||
|
||||
// q_string returns a single string value, from the first column of the result of executing `query`, or an error on failure
|
||||
pub fn (db &DB) q_string(query string) !string {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> q_string query: "${query}"')
|
||||
}
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if pres != sqlite_ok {
|
||||
return db.error_message(pres, query)
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
code := C.sqlite3_step(stmt)
|
||||
if code != sqlite_row {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
|
||||
return if val != &u8(unsafe { nil }) { unsafe { tos_clone(val) } } else { '' }
|
||||
}
|
||||
|
||||
// exec_map executes the query on the given `db`, and returns an array of maps of strings, or an error on failure
|
||||
pub fn (db &DB) exec_map(query string) ![]map[string]string {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> exec_map query: "${query}"')
|
||||
}
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if code != sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut col_names := []string{cap: nr_cols}
|
||||
for i in 0 .. nr_cols {
|
||||
col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
|
||||
col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
|
||||
}
|
||||
mut res := 0
|
||||
mut rows := []map[string]string{}
|
||||
for {
|
||||
res = C.sqlite3_step(stmt)
|
||||
if res != sqlite_row {
|
||||
break
|
||||
}
|
||||
mut row := map[string]string{}
|
||||
for i in 0 .. nr_cols {
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
|
||||
row[col_names[i]] = if val != unsafe { nil } { unsafe { tos_clone(val) } } else { '' }
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
fn C.sqlite3_memory_used() i64
|
||||
|
||||
// exec executes the query on the given `db`, and returns an array of all the results, or an error on failure
|
||||
pub fn (db &DB) exec(query string) ![]Row {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> exec query: "${query}"')
|
||||
}
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if code != sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut col_names := []string{cap: nr_cols}
|
||||
for i in 0 .. nr_cols {
|
||||
col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
|
||||
col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
|
||||
}
|
||||
mut res := 0
|
||||
mut rows := []Row{}
|
||||
for {
|
||||
res = C.sqlite3_step(stmt)
|
||||
if res != sqlite_row {
|
||||
break
|
||||
}
|
||||
mut row := Row{
|
||||
names: col_names
|
||||
}
|
||||
for i in 0 .. nr_cols {
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
|
||||
if val == &u8(unsafe { nil }) {
|
||||
row.vals << ''
|
||||
} else {
|
||||
row.vals << unsafe { tos_clone(val) }
|
||||
}
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// exec_one executes a query on the given `db`.
|
||||
// It returns either the first row from the result, if the query was successful, or an error.
|
||||
@[manualfree]
|
||||
pub fn (db &DB) exec_one(query string) !Row {
|
||||
rows := db.exec(query)!
|
||||
defer {
|
||||
unsafe { rows.free() }
|
||||
}
|
||||
if rows.len == 0 {
|
||||
return &SQLError{
|
||||
msg: 'No rows'
|
||||
code: sqlite_done
|
||||
}
|
||||
}
|
||||
res := rows[0]
|
||||
return res
|
||||
}
|
||||
|
||||
// error_message returns a proper V error, given an integer error code received from SQLite, and a query string
|
||||
@[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})'
|
||||
unsafe { errmsg.free() }
|
||||
return SQLError{
|
||||
msg: msg
|
||||
code: code
|
||||
}
|
||||
}
|
||||
|
||||
// exec_none executes a query, and returns the integer SQLite result code.
|
||||
// Use it, in case you don't expect any row results, but still want a result code.
|
||||
// e.g. for queries like these: `INSERT INTO ... VALUES (...)`
|
||||
pub fn (db &DB) exec_none(query string) int {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> exec_none query: "${query}"')
|
||||
}
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if pres != sqlite_ok {
|
||||
return -1
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
code := C.sqlite3_step(stmt)
|
||||
return code
|
||||
}
|
||||
|
||||
// exec_param_many executes a query with parameters provided as ?,
|
||||
// and returns either an error on failure, or the full result set on success
|
||||
pub fn (db &DB) exec_param_many(query string, params Params) ![]Row {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> exec_param_many query: "${query}", params: ${params}')
|
||||
}
|
||||
mut stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), -1, &stmt, 0)
|
||||
if code != sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
|
||||
mut rows := []Row{}
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut col_names := []string{cap: nr_cols}
|
||||
for i in 0 .. nr_cols {
|
||||
col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
|
||||
col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
|
||||
}
|
||||
|
||||
if params is []string {
|
||||
for i, param in params {
|
||||
code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
|
||||
if code != sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
}
|
||||
for {
|
||||
mut row := Row{
|
||||
names: col_names
|
||||
}
|
||||
code = C.sqlite3_step(stmt)
|
||||
if is_error(code) {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
if code == sqlite_done {
|
||||
break
|
||||
}
|
||||
for i in 0 .. nr_cols {
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
|
||||
if val == &u8(unsafe { nil }) {
|
||||
row.vals << ''
|
||||
} else {
|
||||
row.vals << unsafe { tos_clone(val) }
|
||||
}
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
} else if params is [][]string {
|
||||
// Rows to process
|
||||
for params_row in params {
|
||||
mut row := Row{
|
||||
names: col_names
|
||||
}
|
||||
// Param values to bind
|
||||
for i, param in params_row {
|
||||
code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
|
||||
if code != sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
}
|
||||
for {
|
||||
code = C.sqlite3_step(stmt)
|
||||
if is_error(code) {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
if code == sqlite_done {
|
||||
break
|
||||
}
|
||||
for i in 0 .. nr_cols {
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
|
||||
if val == &u8(unsafe { nil }) {
|
||||
row.vals << ''
|
||||
} else {
|
||||
row.vals << unsafe { tos_clone(val) }
|
||||
}
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
C.sqlite3_reset(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
// exec_param executes a query with one parameter provided as a ?,
|
||||
// and returns either an error on failure, or the full result set on success
|
||||
pub fn (db &DB) exec_param(query string, param string) ![]Row {
|
||||
return db.exec_param_many(query, [param])
|
||||
}
|
||||
|
||||
// exec_param2 executes a query with two parameters provided as ? placeholders.
|
||||
pub fn (db &DB) exec_param2(query string, param string, param2 string) ![]Row {
|
||||
return db.exec_param_many(query, [param, param2])
|
||||
}
|
||||
|
||||
// create_table issues a "create table if not exists" command to the db.
|
||||
// It creates the table named 'table_name', with columns generated from 'columns' array.
|
||||
// The default columns type will be TEXT.
|
||||
pub fn (mut db DB) create_table(table_name string, columns []string) ! {
|
||||
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')!
|
||||
}
|
||||
|
||||
// busy_timeout sets 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)
|
||||
pub fn (db &DB) busy_timeout(ms int) int {
|
||||
return C.sqlite3_busy_timeout(db.conn, ms)
|
||||
}
|
||||
|
||||
// synchronization_mode sets disk synchronization mode, which controls how
|
||||
// aggressively SQLite will write data to physical storage.
|
||||
// If the command fails to execute an error is returned
|
||||
// .off: No syncs at all. (fastest)
|
||||
// .normal: Sync after each sequence of critical disk operations.
|
||||
// .full: Sync after each critical disk operation (slowest).
|
||||
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;')!
|
||||
}
|
||||
}
|
||||
|
||||
// journal_mode controls how the journal file is stored and processed.
|
||||
// If the command fails to execute an error is returned
|
||||
// .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.
|
||||
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;')!
|
||||
}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct Sqlite3TransactionParam {
|
||||
transaction_level Sqlite3TransactionLevel = .deferred
|
||||
}
|
||||
|
||||
// begin begins a new transaction.
|
||||
pub fn (mut db DB) begin(param Sqlite3TransactionParam) ! {
|
||||
mut sql_stmt := 'BEGIN '
|
||||
match param.transaction_level {
|
||||
.deferred { sql_stmt += 'DEFERRED;' }
|
||||
.immediate { sql_stmt += 'IMMEDIATE;' }
|
||||
.exclusive { sql_stmt += 'EXCLUSIVE;' }
|
||||
}
|
||||
|
||||
db.exec(sql_stmt)!
|
||||
}
|
||||
|
||||
// savepoint create a new savepoint.
|
||||
pub fn (mut db DB) savepoint(savepoint string) ! {
|
||||
if !savepoint.is_identifier() {
|
||||
return error('savepoint should be a identifier string')
|
||||
}
|
||||
db.exec('SAVEPOINT ${savepoint};')!
|
||||
}
|
||||
|
||||
// commit commits the current transaction.
|
||||
pub fn (mut db DB) commit() ! {
|
||||
db.exec('COMMIT;')!
|
||||
}
|
||||
|
||||
// rollback rollbacks the current transaction.
|
||||
pub fn (mut db DB) rollback() ! {
|
||||
db.exec('ROLLBACK;')!
|
||||
}
|
||||
|
||||
// rollback_to rollbacks to a specified savepoint.
|
||||
pub fn (mut db DB) rollback_to(savepoint string) ! {
|
||||
if !savepoint.is_identifier() {
|
||||
return error('savepoint should be a identifier string')
|
||||
}
|
||||
db.exec('ROLLBACK TO ${savepoint};')!
|
||||
}
|
||||
|
||||
// release_savepoint releases a specified savepoint.
|
||||
pub fn (mut db DB) release_savepoint(savepoint string) ! {
|
||||
if !savepoint.is_identifier() {
|
||||
return error('savepoint should be a identifier string')
|
||||
}
|
||||
db.exec('RELEASE SAVEPOINT ${savepoint};')!
|
||||
}
|
||||
|
||||
// tables returns the names of all user tables in the database.
|
||||
pub fn (db &DB) tables() ![]string {
|
||||
rows :=
|
||||
db.exec("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")!
|
||||
return rows.map(it.vals[0])
|
||||
}
|
||||
|
||||
// columns returns the column names for the given table.
|
||||
pub fn (db &DB) columns(table string) ![]string {
|
||||
escaped := table.replace('"', '""')
|
||||
rows := db.exec('PRAGMA table_info("${escaped}")')!
|
||||
return rows.map(it.vals[1])
|
||||
}
|
||||
|
||||
// schema returns the CREATE statement(s) for the given table, or for all
|
||||
// objects if table is empty.
|
||||
pub fn (db &DB) schema(table string) !string {
|
||||
filter := if table != '' {
|
||||
escaped := table.replace("'", "''")
|
||||
"AND name='${escaped}'"
|
||||
} else {
|
||||
''
|
||||
}
|
||||
rows :=
|
||||
db.exec("SELECT sql FROM sqlite_master WHERE type IN ('table','index','view','trigger') ${filter} AND sql IS NOT NULL ORDER BY type, name")!
|
||||
return rows.map(it.vals[0]).join('\n\n')
|
||||
}
|
||||
|
||||
// db_size returns the database file size in bytes, computed from page_count
|
||||
// and page_size.
|
||||
pub fn (db &DB) db_size() !i64 {
|
||||
pc := db.exec('PRAGMA page_count')!
|
||||
ps := db.exec('PRAGMA page_size')!
|
||||
if pc.len == 0 || ps.len == 0 {
|
||||
return 0
|
||||
}
|
||||
return pc[0].vals[0].i64() * ps[0].vals[0].i64()
|
||||
}
|
||||
|
||||
// reset returns the connection to initial state for reuse
|
||||
pub fn (mut db DB) reset() ! {
|
||||
}
|
||||
|
||||
// validate checks if the connection is still usable
|
||||
pub fn (mut db DB) validate() !bool {
|
||||
return db.exec_none('SELECT 1') == 100
|
||||
}
|
||||
Reference in new issue
Block a user