0 issues 1 contributor 4 branches 0 releases
Additions: 312 Deletions: 86 View patch
1 'vlib/net/udp_test.v',
2 'vlib/net/tcp_test.v',
3 'vlib/orm/orm_test.v',
4+ 'vlib/orm/orm_sql_or_blocks_test.v',
5 'vlib/sqlite/sqlite_test.v',
6 'vlib/sqlite/sqlite_orm_test.v',
7 'vlib/v/tests/orm_sub_struct_test.v',
8 'vlib/sqlite/sqlite_test.v',
9 'vlib/sqlite/sqlite_orm_test.v',
10 'vlib/orm/orm_test.v',
11+ 'vlib/orm/orm_sql_or_blocks_test.v',
12 'vlib/v/tests/orm_sub_struct_test.v',
13 'vlib/v/tests/orm_sub_array_struct_test.v',
14 'vlib/v/tests/orm_joined_tables_select_test.v',
15
1new file mode 100644
2+import os
3+import sqlite
4+
5+struct User {
6+ id i64 [primary; sql: serial]
7+ name string [unique]
8+}
9+
10+const db_path = os.join_path(os.temp_dir(), 'sql_statement_or_blocks.db')
11+
12+fn test_ensure_db_exists_and_user_table_is_ok() ? {
13+ db := sqlite.connect(db_path)?
14+ assert true
15+
16+ eprintln('> drop pre-existing User table...')
17+ db.exec('drop table if exists User')
18+
19+ eprintln('> creating User table...')
20+ sql db {
21+ create table User
22+ } or { panic(err) }
23+ assert true
24+}
25+
26+fn test_sql_or_block_for_insert() ? {
27+ db := sqlite.connect(db_path)?
28+ user := User{1, 'bilbo'}
29+
30+ eprintln('> inserting user 1 (first try)...')
31+ sql db {
32+ insert user into User
33+ } or {
34+ println('user should have been inserted, but could not, err: $err')
35+ assert false
36+ }
37+
38+ eprintln('> inserting user 1 (second try)...')
39+ sql db {
40+ insert user into User
41+ } or {
42+ assert true
43+ println('user could not be inserted, err: $err')
44+ }
45+}
46+
47+fn test_sql_or_block_for_select() ? {
48+ db := sqlite.connect(db_path)?
49+
50+ eprintln('> selecting user with id 1...')
51+ single := sql db {
52+ select from User where id == 1
53+ } or {
54+ eprintln('could not select user, err: $err')
55+ User{0, ''}
56+ }
57+
58+ assert single.id == 1
59+
60+ failed := sql db {
61+ select from User where id == 0
62+ } or {
63+ eprintln('could not select user, err: $err')
64+ User{0, ''}
65+ }
66+
67+ assert failed.id == 0
68+ assert failed.name == ''
69+
70+ eprintln('> selecting users...')
71+ multiple := sql db {
72+ select from User
73+ } or {
74+ eprintln('could not users, err: $err')
75+ []User{}
76+ }
77+
78+ assert multiple.len == 1
79+}
80+
81+fn test_finish() ? {
82+ eprintln('done')
83+ assert true
84+}
85
1 struct C.sqlite3_stmt {
2 }
3
4+[heap]
5 struct Stmt {
6 stmt &C.sqlite3_stmt
7 db &DB
8 }
9
10 //
11+[heap]
12 pub struct DB {
13 pub mut:
14 is_open bool
15 conn &C.sqlite3
16 }
17
18-pub fn (db DB) str() string {
19+pub fn (db &DB) str() string {
20 return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
21 }
22
23
24 // Returns last insert rowid
25 // https://www.sqlite.org/c3ref/last_insert_rowid.html
26-pub fn (db DB) last_insert_rowid() i64 {
27+pub fn (db &DB) last_insert_rowid() i64 {
28 return C.sqlite3_last_insert_rowid(db.conn)
29 }
30
31 // Returns a single cell with value int.
32-pub fn (db DB) q_int(query string) int {
33+pub fn (db &DB) q_int(query string) int {
34 stmt := &C.sqlite3_stmt(0)
35+ defer {
36+ C.sqlite3_finalize(stmt)
37+ }
38 C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
39 C.sqlite3_step(stmt)
40
41 res := C.sqlite3_column_int(stmt, 0)
42- C.sqlite3_finalize(stmt)
43 return res
44 }
45
46 // Returns a single cell with value string.
47-pub fn (db DB) q_string(query string) string {
48+pub fn (db &DB) q_string(query string) string {
49 stmt := &C.sqlite3_stmt(0)
50 defer {
51 C.sqlite3_finalize(stmt)
52
53 // Execute the query on db, return an array of all the results, alongside any result code.
54 // Result codes: https://www.sqlite.org/rescode.html
55-pub fn (db DB) exec(query string) ([]Row, int) {
56+[manualfree]
57+pub fn (db &DB) exec(query string) ([]Row, int) {
58 stmt := &C.sqlite3_stmt(0)
59+ defer {
60+ C.sqlite3_finalize(stmt)
61+ }
62 C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
63 nr_cols := C.sqlite3_column_count(stmt)
64 mut res := 0
65 }
66 rows << row
67 }
68- C.sqlite3_finalize(stmt)
69 return rows, res
70 }
71
72 // Execute a query, handle error code
73 // Return the first row from the resulting table
74-pub fn (db DB) exec_one(query string) ?Row {
75+[manualfree]
76+pub fn (db &DB) exec_one(query string) ?Row {
77 rows, code := db.exec(query)
78+ defer {
79+ unsafe { rows.free() }
80+ }
81 if rows.len == 0 {
82 return IError(&SQLError{
83 msg: 'No rows'
84 code: code
85 })
86 }
87- return rows[0]
88+ res := rows[0]
89+ return res
90 }
91
92-pub fn (db DB) error_message(code int, query string) IError {
93- msg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
94- return IError(&SQLError{
95- msg: '$msg ($code) ($query)'
96+[manualfree]
97+pub fn (db &DB) error_message(code int, query string) IError {
98+ errmsg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
99+ msg := '$errmsg ($code) ($query)'
100+ unsafe { errmsg.free() }
101+ return SQLError{
102+ msg: msg
103 code: code
104- })
105+ }
106 }
107
108 // Execute a query returning only the result code.
109 // In case you don't expect any row results, but still want a result code.
110 // e.g. INSERT INTO ... VALUES (...)
111-pub fn (db DB) exec_none(query string) int {
112+pub fn (db &DB) exec_none(query string) int {
113 stmt := &C.sqlite3_stmt(0)
114 C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
115 code := C.sqlite3_step(stmt)
116
117 /*
118 TODO
119-pub fn (db DB) exec_param(query string, param string) []Row {
120+pub fn (db &DB) exec_param(query string, param string) []Row {
121 }
122 */
123
124 // Issue a "create table if not exists" command to the db.
125 // Creates table named 'table_name', with columns generated from 'columns' array.
126 // Default columns type will be TEXT.
127-pub fn (db DB) create_table(table_name string, columns []string) {
128+pub fn (db &DB) create_table(table_name string, columns []string) {
129 db.exec('create table if not exists $table_name (' + columns.join(',\n') + ')')
130 }
131
132 // Sleeps for a specified amount of time when a table is locked. The handler
133 // will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
134 // (see https://www.sqlite.org/c3ref/busy_timeout.html)
135-pub fn (db DB) busy_timeout(ms int) int {
136+pub fn (db &DB) busy_timeout(ms int) int {
137 return C.sqlite3_busy_timeout(db.conn, ms)
138 }
139
140 // off: No syncs at all. (fastest)
141 // normal: Sync after each sequence of critical disk operations.
142 // full: Sync after each critical disk operation (slowest).
143-pub fn (db DB) synchronization_mode(sync_mode SyncMode) {
144+pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
145 if sync_mode == .off {
146 db.exec('pragma synchronous = OFF;')
147 } else if sync_mode == .full {
148 // delete: At the conclusion of a transaction, journal file is deleted.
149 // truncate: Journal file is truncated to a length of zero bytes.
150 // persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
151-pub fn (db DB) journal_mode(journal_mode JournalMode) {
152+pub fn (db &DB) journal_mode(journal_mode JournalMode) {
153 if journal_mode == .off {
154 db.exec('pragma journal_mode = OFF;')
155 } else if journal_mode == .delete {
156
1 fn C.sqlite3_bind_text(&C.sqlite3_stmt, int, &char, int, voidptr) int
2
3 // Only for V ORM
4-fn (db DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
5+fn (db &DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
6 // println('init_stmt("$query")')
7 stmt := &C.sqlite3_stmt(0)
8 err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
9 return stmt, err
10 }
11
12-fn (db DB) new_init_stmt(query string) ?Stmt {
13+fn (db &DB) new_init_stmt(query string) ?Stmt {
14 stmt, err := db.init_stmt(query)
15 if err != sqlite_ok {
16 return db.error_message(err, query)
17 }
18- return Stmt{stmt, unsafe { &db }}
19+ return Stmt{stmt, db}
20 }
21
22-fn (stmt Stmt) bind_int(idx int, v int) int {
23+fn (stmt &Stmt) bind_int(idx int, v int) int {
24 return C.sqlite3_bind_int(stmt.stmt, idx, v)
25 }
26
27-fn (stmt Stmt) bind_i64(idx int, v i64) int {
28+fn (stmt &Stmt) bind_i64(idx int, v i64) int {
29 return C.sqlite3_bind_int64(stmt.stmt, idx, v)
30 }
31
32-fn (stmt Stmt) bind_f64(idx int, v f64) int {
33+fn (stmt &Stmt) bind_f64(idx int, v f64) int {
34 return C.sqlite3_bind_double(stmt.stmt, idx, v)
35 }
36
37-fn (stmt Stmt) bind_text(idx int, s string) int {
38+fn (stmt &Stmt) bind_text(idx int, s string) int {
39 return C.sqlite3_bind_text(stmt.stmt, idx, voidptr(s.str), s.len, 0)
40 }
41
42-fn (stmt Stmt) get_int(idx int) int {
43+fn (stmt &Stmt) get_int(idx int) int {
44 return C.sqlite3_column_int(stmt.stmt, idx)
45 }
46
47-fn (stmt Stmt) get_i64(idx int) i64 {
48+fn (stmt &Stmt) get_i64(idx int) i64 {
49 return C.sqlite3_column_int64(stmt.stmt, idx)
50 }
51
52-fn (stmt Stmt) get_f64(idx int) f64 {
53+fn (stmt &Stmt) get_f64(idx int) f64 {
54 return C.sqlite3_column_double(stmt.stmt, idx)
55 }
56
57-fn (stmt Stmt) get_text(idx int) string {
58+fn (stmt &Stmt) get_text(idx int) string {
59 b := &char(C.sqlite3_column_text(stmt.stmt, idx))
60
61 if b == &char(0) {
62 return unsafe { b.vstring() }
63 }
64
65-fn (stmt Stmt) get_count() int {
66+fn (stmt &Stmt) get_count() int {
67 return C.sqlite3_column_count(stmt.stmt)
68 }
69
70-fn (stmt Stmt) step() int {
71+fn (stmt &Stmt) step() int {
72 return C.sqlite3_step(stmt.stmt)
73 }
74
75-fn (stmt Stmt) orm_step(query string) ? {
76+fn (stmt &Stmt) orm_step(query string) ? {
77 res := stmt.step()
78 if res != sqlite_ok && res != sqlite_done && res != sqlite_row {
79 return stmt.db.error_message(res, query)
80 }
81 }
82
83-fn (stmt Stmt) finalize() {
84+fn (stmt &Stmt) finalize() {
85 C.sqlite3_finalize(stmt.stmt)
86 }
87
1 pub:
2 pos token.Pos
3 db_expr Expr // `db` in `sql db {`
4+ or_expr OrExpr
5 pub mut:
6 lines []SqlStmtLine
7 }
8
9 pub struct SqlExpr {
10 pub:
11- typ Type
12 is_count bool
13 has_where bool
14 has_order bool
15 has_offset bool
16 has_desc bool
17 is_array bool
18+ or_expr OrExpr
19 pos token.Pos
20 pub mut:
21+ typ Type
22 db_expr Expr // `db` in `sql db {`
23 where_expr Expr
24 order_expr Expr
25
1 c.expr(node.order_expr)
2 }
3 c.expr(node.db_expr)
4+
5+ if node.or_expr.kind == .block {
6+ if node.or_expr.stmts.len == 0 {
7+ c.error('Or block needs to return a default value', node.or_expr.pos)
8+ }
9+ if node.or_expr.stmts.len > 0 && node.or_expr.stmts.last() is ast.ExprStmt {
10+ c.expected_or_type = node.typ
11+ }
12+ c.stmts_ending_with_expression(node.or_expr.stmts)
13+ c.check_expr_opt_call(node, node.typ)
14+ c.expected_or_type = ast.void_type
15+ }
16 return node.typ
17 }
18
19 typ = a
20 }
21 }
22+ if node.or_expr.kind == .block {
23+ for s in node.or_expr.stmts {
24+ c.stmt(s)
25+ }
26+ }
27 return typ
28 }
29
30
1new file mode 100644
2+vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: Or block needs to return a default value
3+ 9 | _ := sql db {
4+ 10 | select from Person
5+ 11 | } or {
6+ | ~~~~
7+ 12 | }
8+ 13 | }
9
1new file mode 100644
2+import sqlite
3+
4+struct Person {
5+ id int [primary; sql: serial]
6+}
7+
8+fn main() {
9+ db := sqlite.connect(':memory:')?
10+ _ := sql db {
11+ select from Person
12+ } or {
13+ }
14+}
15
1 for line in node.lines {
2 f.sql_stmt_line(line)
3 }
4-
5- f.writeln('}')
6+ f.write('}')
7+ f.or_expr(node.or_expr)
8+ f.writeln('')
9 }
10
11 pub fn (mut f Fmt) sql_stmt_line(node ast.SqlStmtLine) {
12 }
13 f.writeln('')
14 f.write('}')
15+ f.or_expr(node.or_expr)
16 }
17
18 pub fn (mut f Fmt) char_literal(node ast.CharLiteral) {
19
1new file mode 100644
2+import sqlite
3+
4+struct User {
5+ id i64 [primary; sql: serial]
6+ name string [unique]
7+}
8+
9+fn main() {
10+ db := sqlite.connect(':memory:')?
11+ sql db {
12+ create table User
13+ } or { panic(err) }
14+ sql db {
15+ insert user into User
16+ } or {
17+ println('user should have been inserted, but could not, err: $err')
18+ exit(1)
19+ }
20+}
21
1 }
2
3 fn (mut g Gen) stmt(node ast.Stmt) {
4+ g.inside_call = false
5 if !g.skip_stmt_pos {
6 g.set_current_pos_as_last_stmt_pos()
7 }
8
1 if node.should_be_skipped {
2 return
3 }
4+ old_inside_call := g.inside_call
5 g.inside_call = true
6 defer {
7- g.inside_call = false
8+ g.inside_call = old_inside_call
9 }
10 gen_keep_alive := node.is_keep_alive && node.return_type != ast.void_type
11 && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt]
12
1 g.expr(node.db_expr)
2 g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
3 for line in node.lines {
4- g.sql_stmt_line(line, conn)
5+ g.sql_stmt_line(line, conn, node.or_expr)
6 }
7 }
8
9-fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
10+fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr) {
11 mut node := nd
12 table_name := g.get_table_name(node.table_expr)
13 g.sql_table_name = g.table.sym(node.table_expr.typ).name
14 res := g.new_tmp_var()
15 mut subs := false
16- mut dcheck := false
17
18 if node.kind != .create {
19 mut fields := []ast.StructField{}
20 node.fields = fields.clone()
21 unsafe { fields.free() }
22 }
23-
24 if node.kind == .create {
25 g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
26 g.sql_create_table(node, expr, table_name)
27 subs = true
28 } else if node.kind == .insert {
29 arr := g.new_tmp_var()
30- g.writeln('Array_orm__Primitive $arr = new_array_from_c_array(0, 0, sizeof(orm__Primitive), NULL);')
31- g.sql_insert(node, expr, table_name, arr, res, '', false, '')
32- dcheck = true
33+ g.writeln('Array_orm__Primitive $arr = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
34+ g.sql_insert(node, expr, table_name, arr, res, '', false, '', or_expr)
35 } else if node.kind == .update {
36 g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
37 g.sql_update(node, expr, table_name)
38 g.write('${option_name}_void $res = orm__Connection_name_table[${expr}._typ]._method_')
39 g.sql_delete(node, expr, table_name)
40 }
41- if !dcheck {
42- g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
43+ if or_expr.kind == .block {
44+ g.or_block(res, or_expr, ast.int_type)
45 }
46 if subs {
47 for _, sub in node.sub_structs {
48- g.sql_stmt_line(sub, expr)
49+ g.sql_stmt_line(sub, expr, or_expr)
50 }
51 }
52 }
53 g.writeln('));')
54 }
55
56-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) {
57+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) {
58 mut subs := []ast.SqlStmtLine{}
59 mut arrs := []ast.SqlStmtLine{}
60 mut fkeys := []string{}
61 fields := node.fields.filter(g.table.sym(it.typ).kind != .array)
62
63 for sub in subs {
64- g.sql_stmt_line(sub, expr)
65+ g.sql_stmt_line(sub, expr, or_expr)
66 g.writeln('array_push(&$last_ids_arr, _MOV((orm__Primitive[]){orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object)}));')
67 }
68
69 g.write('NULL')
70 }
71 g.write('),')
72- g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
73- g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
74- g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
75+ g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
76+ g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
77+ g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
78 g.writeln('});')
79
80- g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
81 if arrs.len > 0 {
82 mut id_name := g.new_tmp_var()
83 g.writeln('orm__Primitive $id_name = orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object);')
84 arr.fields = fff.clone()
85 unsafe { fff.free() }
86 g.sql_insert(arr, expr, g.get_table_name(arr.table_expr), last_ids, res_,
87- id_name, true, fkeys[i])
88+ id_name, true, fkeys[i], or_expr)
89 g.writeln('}')
90 }
91 }
92 // println(expr)
93 // println(node)
94 g.write('update(${expr}._object, _SLIT("$table_name"), (orm__QueryData){')
95- g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
96- g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
97- g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
98- g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
99+ g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
100+ g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
101+ g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
102 if node.updated_columns.len > 0 {
103+ g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
104 g.write(' _MOV((string[$node.updated_columns.len]){')
105 for field in node.updated_columns {
106 g.write('_SLIT("$field"),')
107 }
108 g.write('})')
109 } else {
110- g.write('NULL')
111+ g.write('.fields = __new_array_with_default_noscan($node.updated_columns.len, $node.updated_columns.len, sizeof(string), 0')
112 }
113 g.write('),')
114 g.write('.data = new_array_from_c_array($node.update_exprs.len, $node.update_exprs.len, sizeof(orm__Primitive),')
115 mut data := []ast.Expr{}
116 mut is_and := []bool{}
117 g.sql_where_data(where_expr, mut fields, mut kinds, mut data, mut is_and)
118- g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
119- g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
120+ g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
121 if fields.len > 0 {
122+ g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
123 g.write(' _MOV((string[$fields.len]){')
124 for field in fields {
125 g.write('_SLIT("$field"),')
126 }
127 g.write('})')
128 } else {
129- g.write('NULL')
130+ g.write('.fields = __new_array_with_default_noscan($fields.len, $fields.len, sizeof(string), 0')
131 }
132 g.write('),')
133
134 }
135 g.write('),')
136
137- g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
138 if kinds.len > 0 {
139+ g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
140 g.write(' _MOV((orm__OperationKind[$kinds.len]){')
141 for k in kinds {
142 g.write('$k,')
143 }
144 g.write('})')
145 } else {
146- g.write('NULL')
147+ g.write('.kinds = __new_array_with_default_noscan($kinds.len, $kinds.len, sizeof(orm__OperationKind), 0')
148 }
149 g.write('),')
150
151- g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),')
152 if is_and.len > 0 {
153+ g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),')
154 g.write(' _MOV((bool[$is_and.len]){')
155 for b in is_and {
156 g.write('$b, ')
157 }
158 g.write('})')
159 } else {
160- g.write('NULL')
161+ g.write('.is_and = __new_array_with_default_noscan($is_and.len, $is_and.len, sizeof(bool), 0')
162 }
163 g.write('),}')
164 }
165 g.write('$fn_prefix = &')
166 g.expr(node.db_expr)
167 g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
168- g.sql_select(node, conn, left)
169+ g.sql_select(node, conn, left, node.or_expr)
170 }
171
172-fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
173+fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr ast.OrExpr) {
174 mut fields := []ast.StructField{}
175 mut prim := ''
176 for f in node.fields {
177 exprs << node.offset_expr
178 }
179 g.write('(orm__QueryData) {')
180- g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
181- g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
182- g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
183- g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
184+ g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
185+ g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
186+ g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
187 if exprs.len > 0 {
188+ g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
189 g.write(' _MOV((orm__Primitive[$exprs.len]){')
190 for e in exprs {
191 g.sql_expr_to_orm_primitive(e)
192 }
193 g.write('})')
194 } else {
195- g.write('NULL')
196+ g.write('.data = __new_array_with_default_noscan($exprs.len, $exprs.len, sizeof(orm__Primitive), 0')
197 }
198 g.write(')},')
199
200 if node.has_where {
201 g.sql_gen_where_data(node.where_expr)
202 } else {
203- g.write('(orm__QueryData) {}')
204+ g.write('(orm__QueryData) {')
205+ g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
206+ g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
207+ g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
208+ g.write('.data = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0)')
209+ g.write('}')
210 }
211 g.writeln(');')
212- g.writeln('if (_o${res}.state != 0 && _o${res}.err._typ != _IError_None___index) { _v_panic(IError_str(_o${res}.err)); }')
213+
214+ mut tmp_left := g.new_tmp_var()
215+ g.writeln('${g.typ(node.typ.set_flag(.optional))} $tmp_left;')
216+
217+ if node.or_expr.kind == .block {
218+ g.writeln('${tmp_left}.state = _o${res}.state;')
219+ g.writeln('${tmp_left}.err = _o${res}.err;')
220+ g.or_block(tmp_left, node.or_expr, node.typ)
221+ g.writeln('else {')
222+ g.indent++
223+ }
224+
225 g.writeln('Array_Array_orm__Primitive $res = (*(Array_Array_orm__Primitive*)_o${res}.data);')
226
227 if node.is_count {
228- g.writeln('$left *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);')
229+ g.writeln('*(${g.typ(node.typ)}*) ${tmp_left}.data = *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);')
230+ if node.or_expr.kind == .block {
231+ g.indent--
232+ g.writeln('}')
233+ }
234 } else {
235 tmp := g.new_tmp_var()
236 styp := g.typ(node.typ)
237 typ_str = g.typ(info.elem_type)
238 g.writeln('$styp ${tmp}_array = __new_array(0, ${res}.len, sizeof($typ_str));')
239 g.writeln('for (; $idx < ${res}.len; $idx++) {')
240- g.write('\t$typ_str $tmp = ($typ_str) {')
241+ g.indent++
242+ g.write('$typ_str $tmp = ($typ_str) {')
243 inf := g.table.sym(info.elem_type).struct_info()
244 for i, field in inf.fields {
245 g.zero_struct_field(field)
246 }
247
248 g.writeln('if (${res}.len > 0) {')
249+ g.indent++
250 for i, field in fields {
251 sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get($res, $idx)), $i))'
252 sym := g.table.sym(field.typ)
253 where_expr.right = ident
254 sub.where_expr = where_expr
255
256- g.sql_select(sub, expr, '${tmp}.$field.name = ')
257+ g.sql_select(sub, expr, '${tmp}.$field.name = ', or_expr)
258 } else if sym.kind == .array {
259 mut fkey := ''
260 for attr in field.attrs {
261 where_expr: where_expr
262 }
263
264- g.sql_select(arr, expr, '${tmp}.$field.name = ')
265+ g.sql_select(arr, expr, '${tmp}.$field.name = ', or_expr)
266 } else {
267 mut typ := sym.cname
268 g.writeln('${tmp}.$field.name = *(${sel}._$typ);')
269 }
270 }
271+ g.indent--
272 g.writeln('}')
273
274 if node.is_array {
275 g.writeln('array_push(&${tmp}_array, _MOV(($typ_str[]){ $tmp }));')
276+ g.indent--
277 g.writeln('}')
278 }
279
280- g.write('$left $tmp')
281+ g.write('*(${g.typ(node.typ)}*) ${tmp_left}.data = $tmp')
282 if node.is_array {
283 g.write('_array')
284 }
285- if !g.inside_call {
286- g.writeln(';')
287+ g.writeln(';')
288+ if node.or_expr.kind == .block {
289+ g.indent--
290+ g.writeln('}')
291 }
292 }
293+ g.write('$left *(${g.typ(node.typ)}*) ${tmp_left}.data')
294+ if !g.inside_call {
295+ g.writeln(';')
296+ }
297 }
298
299 fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
300
1 }
2 .name, .question {
3 if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
4- p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
5 node = p.sql_expr()
6- p.inside_match = false
7 } else if p.tok.lit == 'map' && p.peek_tok.kind == .lcbr && !(p.builtin_mod
8 && p.file_base in ['map.v', 'map_d_gcboehm_opt.v']) {
9 p.error_with_pos("deprecated map syntax, use syntax like `{'age': 20}`",
10
1 import v.ast
2
3 fn (mut p Parser) sql_expr() ast.Expr {
4+ tmp_inside_match := p.inside_match
5+ p.inside_match = true
6 // `sql db {`
7 pos := p.tok.pos()
8 p.check_name()
9 typ = table_type
10 }
11 p.check(.rcbr)
12+ p.inside_match = false
13+ or_expr := p.parse_sql_or_block()
14+ p.inside_match = tmp_inside_match
15 return ast.SqlExpr{
16 is_count: is_count
17 typ: typ
18+ or_expr: or_expr
19 db_expr: db_expr
20 where_expr: where_expr
21 has_where: has_where
22 }
23
24 p.next()
25+
26+ mut or_expr := p.parse_sql_or_block()
27+
28 pos.last_line = p.prev_tok.line_nr
29 return ast.SqlStmt{
30 pos: pos.extend(p.prev_tok.pos())
31 db_expr: db_expr
32 lines: lines
33+ or_expr: or_expr
34+ }
35+}
36+
37+fn (mut p Parser) parse_sql_or_block() ast.OrExpr {
38+ mut stmts := []ast.Stmt{}
39+ mut kind := ast.OrKind.absent
40+ mut pos := p.tok.pos()
41+
42+ if p.tok.kind == .key_orelse {
43+ was_inside_or_expr := p.inside_or_expr
44+ p.inside_or_expr = true
45+ p.next()
46+ p.open_scope()
47+ p.scope.register(ast.Var{
48+ name: 'err'
49+ typ: ast.error_type
50+ pos: p.tok.pos()
51+ is_used: true
52+ })
53+ kind = .block
54+ stmts = p.parse_block_no_scope(false)
55+ pos = pos.extend(p.prev_tok.pos())
56+ p.close_scope()
57+ p.inside_or_expr = was_inside_or_expr
58+ }
59+
60+ return ast.OrExpr{
61+ stmts: stmts
62+ kind: kind
63+ pos: pos
64 }
65 }
66