mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
orm: redesign orm (re-write it in V) (#10353)
This commit is contained in:
161
vlib/sqlite/orm.v
Normal file
161
vlib/sqlite/orm.v
Normal file
@@ -0,0 +1,161 @@
|
||||
module sqlite
|
||||
|
||||
import orm
|
||||
import time
|
||||
|
||||
// sql expr
|
||||
|
||||
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive {
|
||||
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
||||
stmt := db.new_init_stmt(query) ?
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, where, query, mut c) ?
|
||||
sqlite_stmt_binder(stmt, data, query, mut c) ?
|
||||
|
||||
defer {
|
||||
stmt.finalize()
|
||||
}
|
||||
|
||||
mut ret := [][]orm.Primitive{}
|
||||
|
||||
if config.is_count {
|
||||
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 {
|
||||
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
|
||||
|
||||
pub fn (db DB) insert(table string, data orm.QueryData) ? {
|
||||
query := orm.orm_stmt_gen(table, '`', .insert, true, '?', 1, data, orm.QueryData{})
|
||||
sqlite_stmt_worker(db, query, data, orm.QueryData{}) ?
|
||||
}
|
||||
|
||||
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ? {
|
||||
query := orm.orm_stmt_gen(table, '`', .update, true, '?', 1, data, where)
|
||||
sqlite_stmt_worker(db, query, data, where) ?
|
||||
}
|
||||
|
||||
pub fn (db DB) delete(table string, where orm.QueryData) ? {
|
||||
query := orm.orm_stmt_gen(table, '`', .delete, true, '?', 1, orm.QueryData{}, where)
|
||||
sqlite_stmt_worker(db, query, orm.QueryData{}, where) ?
|
||||
}
|
||||
|
||||
pub fn (db DB) last_id() orm.Primitive {
|
||||
query := 'SELECT last_insert_rowid();'
|
||||
id := db.q_int(query)
|
||||
return orm.Primitive(id)
|
||||
}
|
||||
|
||||
// table
|
||||
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{}) ?
|
||||
}
|
||||
|
||||
pub fn (db DB) drop(table string) ? {
|
||||
query := 'DROP TABLE `$table`;'
|
||||
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ?
|
||||
}
|
||||
|
||||
// helper
|
||||
|
||||
fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ? {
|
||||
stmt := db.new_init_stmt(query) ?
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, data, query, mut c) ?
|
||||
sqlite_stmt_binder(stmt, where, query, mut c) ?
|
||||
stmt.orm_step(query) ?
|
||||
stmt.finalize()
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(stmt Stmt, c &int, data orm.Primitive) int {
|
||||
mut err := 0
|
||||
match data {
|
||||
i8, i16, int, byte, 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
|
||||
}
|
||||
|
||||
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.string {
|
||||
primitive = stmt.get_text(idx).clone()
|
||||
} else if typ == orm.time {
|
||||
primitive = time.unix(stmt.get_int(idx))
|
||||
} else {
|
||||
return error('Unknown type $typ')
|
||||
}
|
||||
|
||||
return primitive
|
||||
}
|
||||
|
||||
fn sqlite_type_from_v(typ int) ?string {
|
||||
return if typ in orm.nums || typ < 0 || typ in orm.num64 {
|
||||
'INTEGER'
|
||||
} else if typ in orm.float {
|
||||
'REAL'
|
||||
} else if typ == orm.string {
|
||||
'TEXT'
|
||||
} else {
|
||||
error('Unknown type $typ')
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,24 @@ $if windows {
|
||||
|
||||
#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
|
||||
@@ -72,6 +84,8 @@ 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.
|
||||
@@ -106,14 +120,6 @@ pub fn (mut db DB) close() ?bool {
|
||||
return true // successfully closed
|
||||
}
|
||||
|
||||
// Only for V ORM
|
||||
fn (db DB) init_stmt(query string) &C.sqlite3_stmt {
|
||||
// println('init_stmt("$query")')
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
return stmt
|
||||
}
|
||||
|
||||
// Only for V ORM
|
||||
fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
|
||||
x := C.sqlite3_step(stmt)
|
||||
@@ -204,6 +210,14 @@ pub fn (db DB) exec_one(query string) ?Row {
|
||||
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 {
|
||||
@@ -216,8 +230,6 @@ TODO
|
||||
pub fn (db DB) exec_param(query string, param string) []Row {
|
||||
}
|
||||
*/
|
||||
pub fn (db DB) insert<T>(x T) {
|
||||
}
|
||||
|
||||
pub fn (db DB) create_table(table_name string, columns []string) {
|
||||
db.exec('create table if not exists $table_name (' + columns.join(',\n') + ')')
|
||||
|
||||
70
vlib/sqlite/sqlite_orm_test.v
Normal file
70
vlib/sqlite/sqlite_orm_test.v
Normal file
@@ -0,0 +1,70 @@
|
||||
import orm
|
||||
import sqlite
|
||||
|
||||
fn test_sqlite_orm() {
|
||||
sdb := sqlite.connect(':memory:') or { panic(err) }
|
||||
db := orm.Connection(sdb)
|
||||
db.create('Test', [
|
||||
orm.TableField{
|
||||
name: 'id'
|
||||
typ: 7
|
||||
attrs: [
|
||||
StructAttribute{
|
||||
name: 'primary'
|
||||
},
|
||||
StructAttribute{
|
||||
name: 'sql'
|
||||
has_arg: true
|
||||
kind: .plain
|
||||
arg: 'serial'
|
||||
},
|
||||
]
|
||||
},
|
||||
orm.TableField{
|
||||
name: 'name'
|
||||
typ: 18
|
||||
attrs: []
|
||||
},
|
||||
orm.TableField{
|
||||
name: 'age'
|
||||
typ: 8
|
||||
},
|
||||
]) or { panic(err) }
|
||||
|
||||
db.insert('Test', orm.QueryData{
|
||||
fields: ['name', 'age']
|
||||
data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)]
|
||||
}) or { panic(err) }
|
||||
|
||||
res := db.@select(orm.SelectConfig{
|
||||
table: 'Test'
|
||||
has_where: true
|
||||
fields: ['id', 'name', 'age']
|
||||
types: [7, 18, 8]
|
||||
}, orm.QueryData{}, orm.QueryData{
|
||||
fields: ['name', 'age']
|
||||
data: [orm.Primitive('Louis'), i64(100)]
|
||||
types: [18, 8]
|
||||
is_and: [true, true]
|
||||
kinds: [.eq, .eq]
|
||||
}) or { panic(err) }
|
||||
|
||||
id := res[0][0]
|
||||
name := res[0][1]
|
||||
age := res[0][2]
|
||||
|
||||
assert id is int
|
||||
if id is int {
|
||||
assert id == 1
|
||||
}
|
||||
|
||||
assert name is string
|
||||
if name is string {
|
||||
assert name == 'Louis'
|
||||
}
|
||||
|
||||
assert age is i64
|
||||
if age is i64 {
|
||||
assert age == 100
|
||||
}
|
||||
}
|
||||
74
vlib/sqlite/stmt.v
Normal file
74
vlib/sqlite/stmt.v
Normal file
@@ -0,0 +1,74 @@
|
||||
module sqlite
|
||||
|
||||
fn C.sqlite3_bind_double(&C.sqlite3_stmt, int, f64) int
|
||||
fn C.sqlite3_bind_int(&C.sqlite3_stmt, int, int) int
|
||||
fn C.sqlite3_bind_int64(&C.sqlite3_stmt, int, i64) int
|
||||
fn C.sqlite3_bind_text(&C.sqlite3_stmt, int, &char, int, voidptr) int
|
||||
|
||||
// Only for V ORM
|
||||
fn (db DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
|
||||
// println('init_stmt("$query")')
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
fn (db DB) new_init_stmt(query string) ?Stmt {
|
||||
stmt, err := db.init_stmt(query)
|
||||
if err != sqlite_ok {
|
||||
return db.error_message(err, query)
|
||||
}
|
||||
return Stmt{stmt, unsafe { &db }}
|
||||
}
|
||||
|
||||
fn (stmt Stmt) bind_int(idx int, v int) int {
|
||||
return C.sqlite3_bind_int(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) bind_i64(idx int, v i64) int {
|
||||
return C.sqlite3_bind_int64(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) bind_f64(idx int, v f64) int {
|
||||
return C.sqlite3_bind_double(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) bind_text(idx int, s string) int {
|
||||
return C.sqlite3_bind_text(stmt.stmt, idx, voidptr(s.str), s.len, 0)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_int(idx int) int {
|
||||
return C.sqlite3_column_int(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_i64(idx int) i64 {
|
||||
return C.sqlite3_column_int64(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_f64(idx int) f64 {
|
||||
return C.sqlite3_column_double(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_text(idx int) string {
|
||||
b := &char(C.sqlite3_column_text(stmt.stmt, idx))
|
||||
return unsafe { b.vstring() }
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_count() int {
|
||||
return C.sqlite3_column_count(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) step() int {
|
||||
return C.sqlite3_step(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) orm_step(query string) ? {
|
||||
res := stmt.step()
|
||||
if res != sqlite_ok && res != sqlite_done && res != sqlite_row {
|
||||
return stmt.db.error_message(res, query)
|
||||
}
|
||||
}
|
||||
|
||||
fn (stmt Stmt) finalize() {
|
||||
C.sqlite3_finalize(stmt.stmt)
|
||||
}
|
||||
Reference in New Issue
Block a user