2023-01-13 18:02:32 +03:00
|
|
|
module sqlite
|
|
|
|
|
|
|
|
import orm
|
|
|
|
import time
|
|
|
|
|
|
|
|
// @select is used internally by V's ORM for processing `SELECT ` queries
|
|
|
|
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
|
|
|
// 1. Create query and bind necessary data
|
|
|
|
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
|
|
|
$if trace_sqlite ? {
|
|
|
|
eprintln('> @select query: "${query}"')
|
|
|
|
}
|
|
|
|
stmt := db.new_init_stmt(query)!
|
|
|
|
defer {
|
|
|
|
stmt.finalize()
|
|
|
|
}
|
|
|
|
mut c := 1
|
|
|
|
sqlite_stmt_binder(stmt, where, query, mut c)!
|
|
|
|
sqlite_stmt_binder(stmt, data, query, mut c)!
|
|
|
|
|
|
|
|
mut ret := [][]orm.Primitive{}
|
|
|
|
|
|
|
|
if config.is_count {
|
|
|
|
// 2. Get count of returned values & add it to ret array
|
|
|
|
step := stmt.step()
|
|
|
|
if step !in [sqlite_row, sqlite_ok, sqlite_done] {
|
|
|
|
return db.error_message(step, query)
|
|
|
|
}
|
|
|
|
count := stmt.sqlite_select_column(0, 8)!
|
|
|
|
ret << [count]
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
// 2. Parse returned values
|
|
|
|
step := stmt.step()
|
|
|
|
if step == sqlite_done {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if step != sqlite_ok && step != sqlite_row {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
mut row := []orm.Primitive{}
|
|
|
|
for i, typ in config.types {
|
|
|
|
primitive := stmt.sqlite_select_column(i, typ)!
|
|
|
|
row << primitive
|
|
|
|
}
|
|
|
|
ret << row
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// sql stmt
|
|
|
|
|
|
|
|
// insert is used internally by V's ORM for processing `INSERT ` queries
|
|
|
|
pub fn (db DB) insert(table string, data orm.QueryData) ! {
|
2023-02-16 12:34:16 +03:00
|
|
|
query, converted_data := orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?',
|
|
|
|
1, data, orm.QueryData{})
|
2023-01-13 18:02:32 +03:00
|
|
|
sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})!
|
|
|
|
}
|
|
|
|
|
|
|
|
// update is used internally by V's ORM for processing `UPDATE ` queries
|
|
|
|
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
|
2023-02-16 12:34:16 +03:00
|
|
|
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data, where)
|
2023-01-13 18:02:32 +03:00
|
|
|
sqlite_stmt_worker(db, query, data, where)!
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete is used internally by V's ORM for processing `DELETE ` queries
|
|
|
|
pub fn (db DB) delete(table string, where orm.QueryData) ! {
|
2023-02-16 12:34:16 +03:00
|
|
|
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{},
|
|
|
|
where)
|
2023-01-13 18:02:32 +03:00
|
|
|
sqlite_stmt_worker(db, query, orm.QueryData{}, where)!
|
|
|
|
}
|
|
|
|
|
|
|
|
// last_id is used internally by V's ORM for post-processing `INSERT ` queries
|
2023-01-29 17:00:23 +03:00
|
|
|
pub fn (db DB) last_id() int {
|
2023-01-13 18:02:32 +03:00
|
|
|
query := 'SELECT last_insert_rowid();'
|
2023-01-29 17:00:23 +03:00
|
|
|
|
|
|
|
return db.q_int(query)
|
2023-01-13 18:02:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// DDL (table creation/destroying etc)
|
|
|
|
|
|
|
|
// create is used internally by V's ORM for processing table creation queries (DDL)
|
|
|
|
pub fn (db DB) create(table string, fields []orm.TableField) ! {
|
|
|
|
query := orm.orm_table_gen(table, '`', true, 0, fields, sqlite_type_from_v, false) or {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
|
|
|
}
|
|
|
|
|
|
|
|
// drop is used internally by V's ORM for processing table destroying queries (DDL)
|
|
|
|
pub fn (db DB) drop(table string) ! {
|
|
|
|
query := 'DROP TABLE `${table}`;'
|
|
|
|
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper
|
|
|
|
|
|
|
|
// Executes query and bind prepared statement data directly
|
|
|
|
fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ! {
|
|
|
|
$if trace_sqlite ? {
|
|
|
|
eprintln('> sqlite_stmt_worker query: "${query}"')
|
|
|
|
}
|
|
|
|
stmt := db.new_init_stmt(query)!
|
|
|
|
defer {
|
|
|
|
stmt.finalize()
|
|
|
|
}
|
|
|
|
mut c := 1
|
|
|
|
sqlite_stmt_binder(stmt, data, query, mut c)!
|
|
|
|
sqlite_stmt_binder(stmt, where, query, mut c)!
|
|
|
|
stmt.orm_step(query)!
|
|
|
|
}
|
|
|
|
|
|
|
|
// Binds all values of d in the prepared statement
|
|
|
|
fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
|
|
|
|
for data in d.data {
|
|
|
|
err := bind(stmt, c, data)
|
|
|
|
|
|
|
|
if err != 0 {
|
|
|
|
return stmt.db.error_message(err, query)
|
|
|
|
}
|
|
|
|
c++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Universal bind function
|
|
|
|
fn bind(stmt Stmt, c &int, data orm.Primitive) int {
|
|
|
|
mut err := 0
|
|
|
|
match data {
|
|
|
|
i8, i16, int, u8, u16, u32, bool {
|
|
|
|
err = stmt.bind_int(c, int(data))
|
|
|
|
}
|
|
|
|
i64, u64 {
|
|
|
|
err = stmt.bind_i64(c, i64(data))
|
|
|
|
}
|
|
|
|
f32, f64 {
|
|
|
|
err = stmt.bind_f64(c, unsafe { *(&f64(&data)) })
|
|
|
|
}
|
|
|
|
string {
|
|
|
|
err = stmt.bind_text(c, data)
|
|
|
|
}
|
|
|
|
time.Time {
|
|
|
|
err = stmt.bind_int(c, int(data.unix))
|
|
|
|
}
|
|
|
|
orm.InfixType {
|
|
|
|
err = bind(stmt, c, data.right)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Selects column in result and converts it to an orm.Primitive
|
|
|
|
fn (stmt Stmt) sqlite_select_column(idx int, typ int) !orm.Primitive {
|
|
|
|
mut primitive := orm.Primitive(0)
|
|
|
|
|
|
|
|
if typ in orm.nums || typ == -1 {
|
|
|
|
primitive = stmt.get_int(idx)
|
|
|
|
} else if typ in orm.num64 {
|
|
|
|
primitive = stmt.get_i64(idx)
|
|
|
|
} else if typ in orm.float {
|
|
|
|
primitive = stmt.get_f64(idx)
|
|
|
|
} else if typ == orm.type_string {
|
|
|
|
primitive = stmt.get_text(idx).clone()
|
|
|
|
} else if typ == orm.time {
|
|
|
|
d := stmt.get_int(idx)
|
|
|
|
primitive = time.unix(d)
|
|
|
|
} else {
|
|
|
|
return error('Unknown type ${typ}')
|
|
|
|
}
|
|
|
|
|
|
|
|
return primitive
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert type int to sql type string
|
|
|
|
fn sqlite_type_from_v(typ int) !string {
|
|
|
|
return if typ in orm.nums || typ < 0 || typ in orm.num64 || typ == orm.time {
|
|
|
|
'INTEGER'
|
|
|
|
} else if typ in orm.float {
|
|
|
|
'REAL'
|
|
|
|
} else if typ == orm.type_string {
|
|
|
|
'TEXT'
|
|
|
|
} else {
|
|
|
|
error('Unknown type ${typ}')
|
|
|
|
}
|
|
|
|
}
|