mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
orm: init or implementation (#14989)
This commit is contained in:
parent
bc06866b20
commit
6110373519
@ -119,6 +119,7 @@ const (
|
||||
'vlib/net/udp_test.v',
|
||||
'vlib/net/tcp_test.v',
|
||||
'vlib/orm/orm_test.v',
|
||||
'vlib/orm/orm_sql_or_blocks_test.v',
|
||||
'vlib/sqlite/sqlite_test.v',
|
||||
'vlib/sqlite/sqlite_orm_test.v',
|
||||
'vlib/v/tests/orm_sub_struct_test.v',
|
||||
@ -163,6 +164,7 @@ const (
|
||||
'vlib/sqlite/sqlite_test.v',
|
||||
'vlib/sqlite/sqlite_orm_test.v',
|
||||
'vlib/orm/orm_test.v',
|
||||
'vlib/orm/orm_sql_or_blocks_test.v',
|
||||
'vlib/v/tests/orm_sub_struct_test.v',
|
||||
'vlib/v/tests/orm_sub_array_struct_test.v',
|
||||
'vlib/v/tests/orm_joined_tables_select_test.v',
|
||||
|
83
vlib/orm/orm_sql_or_blocks_test.v
Normal file
83
vlib/orm/orm_sql_or_blocks_test.v
Normal file
@ -0,0 +1,83 @@
|
||||
import os
|
||||
import sqlite
|
||||
|
||||
struct User {
|
||||
id i64 [primary; sql: serial]
|
||||
name string [unique]
|
||||
}
|
||||
|
||||
const db_path = os.join_path(os.temp_dir(), 'sql_statement_or_blocks.db')
|
||||
|
||||
fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
assert true
|
||||
|
||||
eprintln('> drop pre-existing User table...')
|
||||
db.exec('drop table if exists User')
|
||||
|
||||
eprintln('> creating User table...')
|
||||
sql db {
|
||||
create table User
|
||||
} or { panic(err) }
|
||||
assert true
|
||||
}
|
||||
|
||||
fn test_sql_or_block_for_insert() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
user := User{1, 'bilbo'}
|
||||
|
||||
eprintln('> inserting user 1 (first try)...')
|
||||
sql db {
|
||||
insert user into User
|
||||
} or {
|
||||
println('user should have been inserted, but could not, err: $err')
|
||||
assert false
|
||||
}
|
||||
|
||||
eprintln('> inserting user 1 (second try)...')
|
||||
sql db {
|
||||
insert user into User
|
||||
} or {
|
||||
assert true
|
||||
println('user could not be inserted, err: $err')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sql_or_block_for_select() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
|
||||
eprintln('> selecting user with id 1...')
|
||||
single := sql db {
|
||||
select from User where id == 1
|
||||
} or {
|
||||
eprintln('could not select user, err: $err')
|
||||
User{0, ''}
|
||||
}
|
||||
|
||||
assert single.id == 1
|
||||
|
||||
failed := sql db {
|
||||
select from User where id == 0
|
||||
} or {
|
||||
eprintln('could not select user, err: $err')
|
||||
User{0, ''}
|
||||
}
|
||||
|
||||
assert failed.id == 0
|
||||
assert failed.name == ''
|
||||
|
||||
eprintln('> selecting users...')
|
||||
multiple := sql db {
|
||||
select from User
|
||||
} or {
|
||||
eprintln('could not users, err: $err')
|
||||
[]User{}
|
||||
}
|
||||
|
||||
assert multiple.len == 1
|
||||
}
|
||||
|
||||
fn test_finish() ? {
|
||||
eprintln('done')
|
||||
assert true
|
||||
}
|
@ -41,6 +41,7 @@ struct C.sqlite3 {
|
||||
struct C.sqlite3_stmt {
|
||||
}
|
||||
|
||||
[heap]
|
||||
struct Stmt {
|
||||
stmt &C.sqlite3_stmt
|
||||
db &DB
|
||||
@ -51,6 +52,7 @@ struct SQLError {
|
||||
}
|
||||
|
||||
//
|
||||
[heap]
|
||||
pub struct DB {
|
||||
pub mut:
|
||||
is_open bool
|
||||
@ -58,7 +60,7 @@ mut:
|
||||
conn &C.sqlite3
|
||||
}
|
||||
|
||||
pub fn (db DB) str() string {
|
||||
pub fn (db &DB) str() string {
|
||||
return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
|
||||
}
|
||||
|
||||
@ -149,23 +151,25 @@ fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
|
||||
|
||||
// Returns last insert rowid
|
||||
// https://www.sqlite.org/c3ref/last_insert_rowid.html
|
||||
pub fn (db DB) last_insert_rowid() i64 {
|
||||
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 {
|
||||
pub fn (db &DB) q_int(query string) int {
|
||||
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)
|
||||
|
||||
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 {
|
||||
pub fn (db &DB) q_string(query string) string {
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
@ -179,8 +183,12 @@ pub fn (db DB) q_string(query string) string {
|
||||
|
||||
// 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) {
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec(query string) ([]Row, int) {
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut res := 0
|
||||
@ -203,14 +211,17 @@ pub fn (db DB) exec(query string) ([]Row, int) {
|
||||
}
|
||||
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 {
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec_one(query string) ?Row {
|
||||
rows, code := db.exec(query)
|
||||
defer {
|
||||
unsafe { rows.free() }
|
||||
}
|
||||
if rows.len == 0 {
|
||||
return IError(&SQLError{
|
||||
msg: 'No rows'
|
||||
@ -222,21 +233,25 @@ pub fn (db DB) exec_one(query string) ?Row {
|
||||
code: code
|
||||
})
|
||||
}
|
||||
return rows[0]
|
||||
res := rows[0]
|
||||
return res
|
||||
}
|
||||
|
||||
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)'
|
||||
[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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (...)
|
||||
pub fn (db DB) exec_none(query string) int {
|
||||
pub fn (db &DB) exec_none(query string) int {
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
code := C.sqlite3_step(stmt)
|
||||
@ -246,14 +261,14 @@ pub fn (db DB) exec_none(query string) int {
|
||||
|
||||
/*
|
||||
TODO
|
||||
pub fn (db DB) exec_param(query string, param string) []Row {
|
||||
pub fn (db &DB) exec_param(query string, param string) []Row {
|
||||
}
|
||||
*/
|
||||
|
||||
// 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.
|
||||
pub fn (db DB) create_table(table_name string, columns []string) {
|
||||
pub fn (db &DB) create_table(table_name string, columns []string) {
|
||||
db.exec('create table if not exists $table_name (' + columns.join(',\n') + ')')
|
||||
}
|
||||
|
||||
@ -261,7 +276,7 @@ pub fn (db DB) create_table(table_name string, columns []string) {
|
||||
// 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 {
|
||||
pub fn (db &DB) busy_timeout(ms int) int {
|
||||
return C.sqlite3_busy_timeout(db.conn, ms)
|
||||
}
|
||||
|
||||
@ -270,7 +285,7 @@ pub fn (db DB) busy_timeout(ms int) int {
|
||||
// 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) {
|
||||
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
|
||||
if sync_mode == .off {
|
||||
db.exec('pragma synchronous = OFF;')
|
||||
} else if sync_mode == .full {
|
||||
@ -286,7 +301,7 @@ pub fn (db DB) synchronization_mode(sync_mode SyncMode) {
|
||||
// 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) {
|
||||
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
|
||||
if journal_mode == .off {
|
||||
db.exec('pragma journal_mode = OFF;')
|
||||
} else if journal_mode == .delete {
|
||||
|
@ -6,50 +6,50 @@ 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) {
|
||||
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 {
|
||||
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 }}
|
||||
return Stmt{stmt, db}
|
||||
}
|
||||
|
||||
fn (stmt Stmt) bind_int(idx int, v int) int {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
fn (stmt &Stmt) get_int(idx int) int {
|
||||
return C.sqlite3_column_int(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_i64(idx int) i64 {
|
||||
fn (stmt &Stmt) get_i64(idx int) i64 {
|
||||
return C.sqlite3_column_int64(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_f64(idx int) f64 {
|
||||
fn (stmt &Stmt) get_f64(idx int) f64 {
|
||||
return C.sqlite3_column_double(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_text(idx int) string {
|
||||
fn (stmt &Stmt) get_text(idx int) string {
|
||||
b := &char(C.sqlite3_column_text(stmt.stmt, idx))
|
||||
|
||||
if b == &char(0) {
|
||||
@ -58,21 +58,21 @@ fn (stmt Stmt) get_text(idx int) string {
|
||||
return unsafe { b.vstring() }
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_count() int {
|
||||
fn (stmt &Stmt) get_count() int {
|
||||
return C.sqlite3_column_count(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) step() int {
|
||||
fn (stmt &Stmt) step() int {
|
||||
return C.sqlite3_step(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt Stmt) orm_step(query string) ? {
|
||||
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() {
|
||||
fn (stmt &Stmt) finalize() {
|
||||
C.sqlite3_finalize(stmt.stmt)
|
||||
}
|
||||
|
@ -1711,6 +1711,7 @@ pub struct SqlStmt {
|
||||
pub:
|
||||
pos token.Pos
|
||||
db_expr Expr // `db` in `sql db {`
|
||||
or_expr OrExpr
|
||||
pub mut:
|
||||
lines []SqlStmtLine
|
||||
}
|
||||
@ -1731,7 +1732,6 @@ pub mut:
|
||||
|
||||
pub struct SqlExpr {
|
||||
pub:
|
||||
typ Type
|
||||
is_count bool
|
||||
has_where bool
|
||||
has_order bool
|
||||
@ -1739,8 +1739,10 @@ pub:
|
||||
has_offset bool
|
||||
has_desc bool
|
||||
is_array bool
|
||||
or_expr OrExpr
|
||||
pos token.Pos
|
||||
pub mut:
|
||||
typ Type
|
||||
db_expr Expr // `db` in `sql db {`
|
||||
where_expr Expr
|
||||
order_expr Expr
|
||||
|
@ -103,6 +103,18 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
||||
c.expr(node.order_expr)
|
||||
}
|
||||
c.expr(node.db_expr)
|
||||
|
||||
if node.or_expr.kind == .block {
|
||||
if node.or_expr.stmts.len == 0 {
|
||||
c.error('Or block needs to return a default value', node.or_expr.pos)
|
||||
}
|
||||
if node.or_expr.stmts.len > 0 && node.or_expr.stmts.last() is ast.ExprStmt {
|
||||
c.expected_or_type = node.typ
|
||||
}
|
||||
c.stmts_ending_with_expression(node.or_expr.stmts)
|
||||
c.check_expr_opt_call(node, node.typ)
|
||||
c.expected_or_type = ast.void_type
|
||||
}
|
||||
return node.typ
|
||||
}
|
||||
|
||||
@ -115,6 +127,11 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type {
|
||||
typ = a
|
||||
}
|
||||
}
|
||||
if node.or_expr.kind == .block {
|
||||
for s in node.or_expr.stmts {
|
||||
c.stmt(s)
|
||||
}
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
|
7
vlib/v/checker/tests/orm_no_default_value.out
Normal file
7
vlib/v/checker/tests/orm_no_default_value.out
Normal file
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: Or block needs to return a default value
|
||||
9 | _ := sql db {
|
||||
10 | select from Person
|
||||
11 | } or {
|
||||
| ~~~~
|
||||
12 | }
|
||||
13 | }
|
13
vlib/v/checker/tests/orm_no_default_value.vv
Normal file
13
vlib/v/checker/tests/orm_no_default_value.vv
Normal file
@ -0,0 +1,13 @@
|
||||
import sqlite
|
||||
|
||||
struct Person {
|
||||
id int [primary; sql: serial]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
db := sqlite.connect(':memory:')?
|
||||
_ := sql db {
|
||||
select from Person
|
||||
} or {
|
||||
}
|
||||
}
|
@ -1256,8 +1256,9 @@ pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) {
|
||||
for line in node.lines {
|
||||
f.sql_stmt_line(line)
|
||||
}
|
||||
|
||||
f.writeln('}')
|
||||
f.write('}')
|
||||
f.or_expr(node.or_expr)
|
||||
f.writeln('')
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) sql_stmt_line(node ast.SqlStmtLine) {
|
||||
@ -2562,6 +2563,7 @@ pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) {
|
||||
}
|
||||
f.writeln('')
|
||||
f.write('}')
|
||||
f.or_expr(node.or_expr)
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) char_literal(node ast.CharLiteral) {
|
||||
|
19
vlib/v/fmt/tests/orm_or_keep.vv
Normal file
19
vlib/v/fmt/tests/orm_or_keep.vv
Normal file
@ -0,0 +1,19 @@
|
||||
import sqlite
|
||||
|
||||
struct User {
|
||||
id i64 [primary; sql: serial]
|
||||
name string [unique]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
db := sqlite.connect(':memory:')?
|
||||
sql db {
|
||||
create table User
|
||||
} or { panic(err) }
|
||||
sql db {
|
||||
insert user into User
|
||||
} or {
|
||||
println('user should have been inserted, but could not, err: $err')
|
||||
exit(1)
|
||||
}
|
||||
}
|
@ -1671,6 +1671,7 @@ fn (mut g Gen) write_v_source_line_info(pos token.Pos) {
|
||||
}
|
||||
|
||||
fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
g.inside_call = false
|
||||
if !g.skip_stmt_pos {
|
||||
g.set_current_pos_as_last_stmt_pos()
|
||||
}
|
||||
|
@ -626,9 +626,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||
if node.should_be_skipped {
|
||||
return
|
||||
}
|
||||
old_inside_call := g.inside_call
|
||||
g.inside_call = true
|
||||
defer {
|
||||
g.inside_call = false
|
||||
g.inside_call = old_inside_call
|
||||
}
|
||||
gen_keep_alive := node.is_keep_alive && node.return_type != ast.void_type
|
||||
&& g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt]
|
||||
|
@ -43,17 +43,16 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
|
||||
g.expr(node.db_expr)
|
||||
g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
|
||||
for line in node.lines {
|
||||
g.sql_stmt_line(line, conn)
|
||||
g.sql_stmt_line(line, conn, node.or_expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
|
||||
fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr) {
|
||||
mut node := nd
|
||||
table_name := g.get_table_name(node.table_expr)
|
||||
g.sql_table_name = g.table.sym(node.table_expr.typ).name
|
||||
res := g.new_tmp_var()
|
||||
mut subs := false
|
||||
mut dcheck := false
|
||||
|
||||
if node.kind != .create {
|
||||
mut fields := []ast.StructField{}
|
||||
@ -75,7 +74,6 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
|
||||
node.fields = fields.clone()
|
||||
unsafe { fields.free() }
|
||||
}
|
||||
|
||||
if node.kind == .create {
|
||||
g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
|
||||
g.sql_create_table(node, expr, table_name)
|
||||
@ -86,9 +84,8 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
|
||||
subs = true
|
||||
} else if node.kind == .insert {
|
||||
arr := g.new_tmp_var()
|
||||
g.writeln('Array_orm__Primitive $arr = new_array_from_c_array(0, 0, sizeof(orm__Primitive), NULL);')
|
||||
g.sql_insert(node, expr, table_name, arr, res, '', false, '')
|
||||
dcheck = true
|
||||
g.writeln('Array_orm__Primitive $arr = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
|
||||
g.sql_insert(node, expr, table_name, arr, res, '', false, '', or_expr)
|
||||
} else if node.kind == .update {
|
||||
g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
|
||||
g.sql_update(node, expr, table_name)
|
||||
@ -96,12 +93,12 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
|
||||
g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
|
||||
g.sql_delete(node, expr, table_name)
|
||||
}
|
||||
if !dcheck {
|
||||
g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
|
||||
if or_expr.kind == .block {
|
||||
g.or_block(res, or_expr, ast.int_type)
|
||||
}
|
||||
if subs {
|
||||
for _, sub in node.sub_structs {
|
||||
g.sql_stmt_line(sub, expr)
|
||||
g.sql_stmt_line(sub, expr, or_expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,7 +144,7 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmtLine, expr string, table_name st
|
||||
g.writeln('));')
|
||||
}
|
||||
|
||||
fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, is_array bool, fkey string) {
|
||||
fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, is_array bool, fkey string, or_expr ast.OrExpr) {
|
||||
mut subs := []ast.SqlStmtLine{}
|
||||
mut arrs := []ast.SqlStmtLine{}
|
||||
mut fkeys := []string{}
|
||||
@ -181,7 +178,7 @@ fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string,
|
||||
fields := node.fields.filter(g.table.sym(it.typ).kind != .array)
|
||||
|
||||
for sub in subs {
|
||||
g.sql_stmt_line(sub, expr)
|
||||
g.sql_stmt_line(sub, expr, or_expr)
|
||||
g.writeln('array_push(&$last_ids_arr, _MOV((orm__Primitive[]){orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object)}));')
|
||||
}
|
||||
|
||||
@ -226,12 +223,11 @@ fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string,
|
||||
g.write('NULL')
|
||||
}
|
||||
g.write('),')
|
||||
g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
|
||||
g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
|
||||
g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.writeln('});')
|
||||
|
||||
g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
|
||||
if arrs.len > 0 {
|
||||
mut id_name := g.new_tmp_var()
|
||||
g.writeln('orm__Primitive $id_name = orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object);')
|
||||
@ -263,7 +259,7 @@ fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string,
|
||||
arr.fields = fff.clone()
|
||||
unsafe { fff.free() }
|
||||
g.sql_insert(arr, expr, g.get_table_name(arr.table_expr), last_ids, res_,
|
||||
id_name, true, fkeys[i])
|
||||
id_name, true, fkeys[i], or_expr)
|
||||
g.writeln('}')
|
||||
}
|
||||
}
|
||||
@ -274,18 +270,18 @@ fn (mut g Gen) sql_update(node ast.SqlStmtLine, expr string, table_name string)
|
||||
// println(expr)
|
||||
// println(node)
|
||||
g.write('update(${expr}._object, _SLIT("$table_name"), (orm__QueryData){')
|
||||
g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
|
||||
g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
|
||||
g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
|
||||
g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
if node.updated_columns.len > 0 {
|
||||
g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
|
||||
g.write(' _MOV((string[$node.updated_columns.len]){')
|
||||
for field in node.updated_columns {
|
||||
g.write('_SLIT("$field"),')
|
||||
}
|
||||
g.write('})')
|
||||
} else {
|
||||
g.write('NULL')
|
||||
g.write('.fields = __new_array_with_default_noscan($node.updated_columns.len, $node.updated_columns.len, sizeof(string), 0')
|
||||
}
|
||||
g.write('),')
|
||||
g.write('.data = new_array_from_c_array($node.update_exprs.len, $node.update_exprs.len, sizeof(orm__Primitive),')
|
||||
@ -453,16 +449,16 @@ fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) {
|
||||
mut data := []ast.Expr{}
|
||||
mut is_and := []bool{}
|
||||
g.sql_where_data(where_expr, mut fields, mut kinds, mut data, mut is_and)
|
||||
g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
|
||||
g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
if fields.len > 0 {
|
||||
g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
|
||||
g.write(' _MOV((string[$fields.len]){')
|
||||
for field in fields {
|
||||
g.write('_SLIT("$field"),')
|
||||
}
|
||||
g.write('})')
|
||||
} else {
|
||||
g.write('NULL')
|
||||
g.write('.fields = __new_array_with_default_noscan($fields.len, $fields.len, sizeof(string), 0')
|
||||
}
|
||||
g.write('),')
|
||||
|
||||
@ -476,27 +472,27 @@ fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) {
|
||||
}
|
||||
g.write('),')
|
||||
|
||||
g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
|
||||
if kinds.len > 0 {
|
||||
g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
|
||||
g.write(' _MOV((orm__OperationKind[$kinds.len]){')
|
||||
for k in kinds {
|
||||
g.write('$k,')
|
||||
}
|
||||
g.write('})')
|
||||
} else {
|
||||
g.write('NULL')
|
||||
g.write('.kinds = __new_array_with_default_noscan($kinds.len, $kinds.len, sizeof(orm__OperationKind), 0')
|
||||
}
|
||||
g.write('),')
|
||||
|
||||
g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),')
|
||||
if is_and.len > 0 {
|
||||
g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),')
|
||||
g.write(' _MOV((bool[$is_and.len]){')
|
||||
for b in is_and {
|
||||
g.write('$b, ')
|
||||
}
|
||||
g.write('})')
|
||||
} else {
|
||||
g.write('NULL')
|
||||
g.write('.is_and = __new_array_with_default_noscan($is_and.len, $is_and.len, sizeof(bool), 0')
|
||||
}
|
||||
g.write('),}')
|
||||
}
|
||||
@ -527,10 +523,10 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
|
||||
g.write('$fn_prefix = &')
|
||||
g.expr(node.db_expr)
|
||||
g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
|
||||
g.sql_select(node, conn, left)
|
||||
g.sql_select(node, conn, left, node.or_expr)
|
||||
}
|
||||
|
||||
fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr ast.OrExpr) {
|
||||
mut fields := []ast.StructField{}
|
||||
mut prim := ''
|
||||
for f in node.fields {
|
||||
@ -615,32 +611,52 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
exprs << node.offset_expr
|
||||
}
|
||||
g.write('(orm__QueryData) {')
|
||||
g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
|
||||
g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
|
||||
g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
|
||||
g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
if exprs.len > 0 {
|
||||
g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
|
||||
g.write(' _MOV((orm__Primitive[$exprs.len]){')
|
||||
for e in exprs {
|
||||
g.sql_expr_to_orm_primitive(e)
|
||||
}
|
||||
g.write('})')
|
||||
} else {
|
||||
g.write('NULL')
|
||||
g.write('.data = __new_array_with_default_noscan($exprs.len, $exprs.len, sizeof(orm__Primitive), 0')
|
||||
}
|
||||
g.write(')},')
|
||||
|
||||
if node.has_where {
|
||||
g.sql_gen_where_data(node.where_expr)
|
||||
} else {
|
||||
g.write('(orm__QueryData) {}')
|
||||
g.write('(orm__QueryData) {')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.write('.data = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0)')
|
||||
g.write('}')
|
||||
}
|
||||
g.writeln(');')
|
||||
g.writeln('if (_o${res}.state != 0 && _o${res}.err._typ != _IError_None___index) { _v_panic(IError_str(_o${res}.err)); }')
|
||||
|
||||
mut tmp_left := g.new_tmp_var()
|
||||
g.writeln('${g.typ(node.typ.set_flag(.optional))} $tmp_left;')
|
||||
|
||||
if node.or_expr.kind == .block {
|
||||
g.writeln('${tmp_left}.state = _o${res}.state;')
|
||||
g.writeln('${tmp_left}.err = _o${res}.err;')
|
||||
g.or_block(tmp_left, node.or_expr, node.typ)
|
||||
g.writeln('else {')
|
||||
g.indent++
|
||||
}
|
||||
|
||||
g.writeln('Array_Array_orm__Primitive $res = (*(Array_Array_orm__Primitive*)_o${res}.data);')
|
||||
|
||||
if node.is_count {
|
||||
g.writeln('$left *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);')
|
||||
g.writeln('*(${g.typ(node.typ)}*) ${tmp_left}.data = *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);')
|
||||
if node.or_expr.kind == .block {
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
}
|
||||
} else {
|
||||
tmp := g.new_tmp_var()
|
||||
styp := g.typ(node.typ)
|
||||
@ -652,7 +668,8 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
typ_str = g.typ(info.elem_type)
|
||||
g.writeln('$styp ${tmp}_array = __new_array(0, ${res}.len, sizeof($typ_str));')
|
||||
g.writeln('for (; $idx < ${res}.len; $idx++) {')
|
||||
g.write('\t$typ_str $tmp = ($typ_str) {')
|
||||
g.indent++
|
||||
g.write('$typ_str $tmp = ($typ_str) {')
|
||||
inf := g.table.sym(info.elem_type).struct_info()
|
||||
for i, field in inf.fields {
|
||||
g.zero_struct_field(field)
|
||||
@ -674,6 +691,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
}
|
||||
|
||||
g.writeln('if (${res}.len > 0) {')
|
||||
g.indent++
|
||||
for i, field in fields {
|
||||
sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get($res, $idx)), $i))'
|
||||
sym := g.table.sym(field.typ)
|
||||
@ -692,7 +710,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
where_expr.right = ident
|
||||
sub.where_expr = where_expr
|
||||
|
||||
g.sql_select(sub, expr, '${tmp}.$field.name = ')
|
||||
g.sql_select(sub, expr, '${tmp}.$field.name = ', or_expr)
|
||||
} else if sym.kind == .array {
|
||||
mut fkey := ''
|
||||
for attr in field.attrs {
|
||||
@ -740,27 +758,35 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
|
||||
where_expr: where_expr
|
||||
}
|
||||
|
||||
g.sql_select(arr, expr, '${tmp}.$field.name = ')
|
||||
g.sql_select(arr, expr, '${tmp}.$field.name = ', or_expr)
|
||||
} else {
|
||||
mut typ := sym.cname
|
||||
g.writeln('${tmp}.$field.name = *(${sel}._$typ);')
|
||||
}
|
||||
}
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
|
||||
if node.is_array {
|
||||
g.writeln('array_push(&${tmp}_array, _MOV(($typ_str[]){ $tmp }));')
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
}
|
||||
|
||||
g.write('$left $tmp')
|
||||
g.write('*(${g.typ(node.typ)}*) ${tmp_left}.data = $tmp')
|
||||
if node.is_array {
|
||||
g.write('_array')
|
||||
}
|
||||
if !g.inside_call {
|
||||
g.writeln(';')
|
||||
g.writeln(';')
|
||||
if node.or_expr.kind == .block {
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
}
|
||||
}
|
||||
g.write('$left *(${g.typ(node.typ)}*) ${tmp_left}.data')
|
||||
if !g.inside_call {
|
||||
g.writeln(';')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
|
||||
|
@ -47,9 +47,7 @@ pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr {
|
||||
}
|
||||
.name, .question {
|
||||
if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
|
||||
p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
|
||||
node = p.sql_expr()
|
||||
p.inside_match = false
|
||||
} else if p.tok.lit == 'map' && p.peek_tok.kind == .lcbr && !(p.builtin_mod
|
||||
&& p.file_base in ['map.v', 'map_d_gcboehm_opt.v']) {
|
||||
p.error_with_pos("deprecated map syntax, use syntax like `{'age': 20}`",
|
||||
|
@ -6,6 +6,8 @@ module parser
|
||||
import v.ast
|
||||
|
||||
fn (mut p Parser) sql_expr() ast.Expr {
|
||||
tmp_inside_match := p.inside_match
|
||||
p.inside_match = true
|
||||
// `sql db {`
|
||||
pos := p.tok.pos()
|
||||
p.check_name()
|
||||
@ -91,9 +93,13 @@ fn (mut p Parser) sql_expr() ast.Expr {
|
||||
typ = table_type
|
||||
}
|
||||
p.check(.rcbr)
|
||||
p.inside_match = false
|
||||
or_expr := p.parse_sql_or_block()
|
||||
p.inside_match = tmp_inside_match
|
||||
return ast.SqlExpr{
|
||||
is_count: is_count
|
||||
typ: typ
|
||||
or_expr: or_expr
|
||||
db_expr: db_expr
|
||||
where_expr: where_expr
|
||||
has_where: has_where
|
||||
@ -136,11 +142,45 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
||||
}
|
||||
|
||||
p.next()
|
||||
|
||||
mut or_expr := p.parse_sql_or_block()
|
||||
|
||||
pos.last_line = p.prev_tok.line_nr
|
||||
return ast.SqlStmt{
|
||||
pos: pos.extend(p.prev_tok.pos())
|
||||
db_expr: db_expr
|
||||
lines: lines
|
||||
or_expr: or_expr
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_sql_or_block() ast.OrExpr {
|
||||
mut stmts := []ast.Stmt{}
|
||||
mut kind := ast.OrKind.absent
|
||||
mut pos := p.tok.pos()
|
||||
|
||||
if p.tok.kind == .key_orelse {
|
||||
was_inside_or_expr := p.inside_or_expr
|
||||
p.inside_or_expr = true
|
||||
p.next()
|
||||
p.open_scope()
|
||||
p.scope.register(ast.Var{
|
||||
name: 'err'
|
||||
typ: ast.error_type
|
||||
pos: p.tok.pos()
|
||||
is_used: true
|
||||
})
|
||||
kind = .block
|
||||
stmts = p.parse_block_no_scope(false)
|
||||
pos = pos.extend(p.prev_tok.pos())
|
||||
p.close_scope()
|
||||
p.inside_or_expr = was_inside_or_expr
|
||||
}
|
||||
|
||||
return ast.OrExpr{
|
||||
stmts: stmts
|
||||
kind: kind
|
||||
pos: pos
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user