v2 / vlib / v / gen / c / orm.v
2746 lines · 2586 sloc · 90.49 KB · d5578efae67ef01ab3a63866d429b7dacf6c237d
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module c
4
5import v.ast
6import v.util
7
8// orm_field_access_name converts an ORM field name to proper C struct member access.
9// For embedded struct fields like "Payload.some_field", this returns "Payload.some_field"
10// (keeping the dot for C member access). For regular fields, it uses c_name().
11fn orm_field_access_name(field_name string) string {
12 if field_name.contains('.') {
13 // Embedded struct field - keep the dots for proper C struct member access
14 // e.g., "Payload.some_field" -> "Payload.some_field"
15 parts := field_name.split('.')
16 mut result := []string{}
17 for part in parts {
18 result << c_name(part)
19 }
20 return result.join('.')
21 }
22 return c_name(field_name)
23}
24
25fn (g &Gen) orm_inserting_object_type(obj ast.ScopeObject) ast.Type {
26 return match obj {
27 ast.Var {
28 if obj.smartcasts.len > 0 && obj.orig_type != 0
29 && g.table.final_sym(g.table.final_type(obj.orig_type)).kind == .sum_type {
30 obj.orig_type
31 } else {
32 obj.typ
33 }
34 }
35 else {
36 ast.void_type
37 }
38 }
39}
40
41fn (g &Gen) orm_primitive_field_name(typ ast.Type) string {
42 final_typ := g.table.final_type(typ.clear_flag(.option))
43 sym := g.table.sym(final_typ)
44 if sym.name == 'time.Time' {
45 return 'time'
46 }
47 if sym.kind == .enum {
48 return 'i64'
49 }
50 if sym.kind == .array {
51 return vint2int(sym.cname.to_lower())
52 }
53 return vint2int(sym.cname)
54}
55
56fn (g &Gen) orm_non_array_fields(fields []ast.StructField) []ast.StructField {
57 mut non_array_fields := []ast.StructField{cap: fields.len}
58 for field in fields {
59 final_typ := g.table.final_type(field.typ.clear_flag(.option))
60 if g.table.sym(final_typ).kind != .array {
61 non_array_fields << field
62 }
63 }
64 return non_array_fields
65}
66
67fn (g &Gen) orm_primitive_variant_field_name(typ ast.Type) string {
68 final_typ := g.table.final_type(typ.clear_flag(.option))
69 sym := g.table.sym(final_typ)
70 if sym.kind == .enum {
71 return 'i64'
72 }
73 return vint2int(sym.cname)
74}
75
76enum SqlExprSide {
77 left
78 right
79}
80
81fn sql_query_data_field_name(expr ast.Expr) string {
82 return match expr {
83 ast.Ident { expr.name }
84 ast.SelectorExpr { '${sql_query_data_field_name(expr.expr)}.${expr.field_name}' }
85 ast.ParExpr { sql_query_data_field_name(expr.expr) }
86 else { '' }
87 }
88}
89
90fn sql_query_data_op_kind(expr ast.InfixExpr) string {
91 is_nil_comparison := expr.right is ast.Nil && expr.op in [.eq, .ne]
92 return match expr.op {
93 .ne {
94 if is_nil_comparison {
95 'orm__OperationKind__is_not_null'
96 } else {
97 'orm__OperationKind__neq'
98 }
99 }
100 .eq {
101 if is_nil_comparison {
102 'orm__OperationKind__is_null'
103 } else {
104 'orm__OperationKind__eq'
105 }
106 }
107 .lt {
108 'orm__OperationKind__lt'
109 }
110 .gt {
111 'orm__OperationKind__gt'
112 }
113 .ge {
114 'orm__OperationKind__ge'
115 }
116 .le {
117 'orm__OperationKind__le'
118 }
119 .key_like {
120 'orm__OperationKind__orm_like'
121 }
122 .key_ilike {
123 'orm__OperationKind__orm_ilike'
124 }
125 .key_is {
126 'orm__OperationKind__is_null'
127 }
128 .not_is {
129 'orm__OperationKind__is_not_null'
130 }
131 .key_in {
132 'orm__OperationKind__in'
133 }
134 .not_in {
135 'orm__OperationKind__not_in'
136 }
137 else {
138 'orm__OperationKind__eq'
139 }
140 }
141}
142
143fn (g &Gen) resolve_sql_query_data_expr(expr ast.Expr) ?ast.SqlQueryDataExpr {
144 mut current := expr
145 for {
146 current = current.remove_par()
147 mut next_expr := current
148 mut has_next_expr := false
149 match current {
150 ast.SqlQueryDataExpr {
151 return current as ast.SqlQueryDataExpr
152 }
153 ast.Ident {
154 obj := current.obj
155 match obj {
156 ast.Var {
157 if obj.is_mut {
158 return none
159 }
160 next_expr = obj.expr
161 has_next_expr = true
162 }
163 ast.ConstField {
164 next_expr = obj.expr
165 has_next_expr = true
166 }
167 else {}
168 }
169 }
170 else {
171 return none
172 }
173 }
174
175 if has_next_expr {
176 current = next_expr
177 continue
178 }
179 return none
180 }
181 return none
182}
183
184fn (mut g Gen) emit_sql_query_data(node ast.SqlQueryDataExpr, resolve_columns bool) string {
185 query_var := g.new_tmp_var()
186 g.writeln('orm__QueryData ${query_var} = (orm__QueryData){')
187 g.indent++
188 g.writeln('.fields = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
189 g.writeln('.data = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0),')
190 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
191 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
192 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
193 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
194 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
195 g.indent--
196 g.writeln('};')
197 g.emit_sql_query_data_items(query_var, node.items, resolve_columns)
198 return query_var
199}
200
201fn (mut g Gen) emit_dynamic_sql_query_data(expr ast.Expr) string {
202 node := g.resolve_sql_query_data_expr(expr) or {
203 verror('ORM: expected a dynamic query-data block or immutable alias')
204 }
205 return g.emit_sql_query_data(node, true)
206}
207
208struct SqlQueryDataGuardState {
209 temp_var string
210 expr_type ast.Type
211}
212
213fn (mut g Gen) sql_query_data_guard_expr_type(cond ast.IfGuardExpr) ast.Type {
214 mut expr_type := cond.expr_type
215 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
216 if cond.expr is ast.CallExpr {
217 resolved := g.resolve_return_type(cond.expr)
218 if resolved != ast.void_type && !resolved.has_flag(.generic) {
219 expr_type = if cond.expr_type.has_flag(.option) && !resolved.has_flag(.option) {
220 resolved.set_flag(.option)
221 } else if cond.expr_type.has_flag(.result) && !resolved.has_flag(.result) {
222 resolved.set_flag(.result)
223 } else {
224 resolved
225 }
226 }
227 } else {
228 resolved := g.unwrap_generic(g.recheck_concrete_type(expr_type))
229 if resolved != ast.void_type {
230 expr_type = resolved
231 }
232 }
233 }
234 return expr_type
235}
236
237fn (mut g Gen) emit_sql_query_data_guard_open(cond ast.IfGuardExpr) SqlQueryDataGuardState {
238 expr_type := g.sql_query_data_guard_expr_type(cond)
239 temp_var := g.new_tmp_var()
240 g.writeln('${g.styp(g.unwrap_generic(expr_type))} ${temp_var} = {0};')
241 g.write('if ((${temp_var} = ')
242 g.expr(cond.expr)
243 if expr_type.has_flag(.option) {
244 dot_or_ptr := if !expr_type.has_flag(.option_mut_param_t) {
245 '.'
246 } else {
247 '-> '
248 }
249 g.writeln('), ${temp_var}${dot_or_ptr}state == 0) {')
250 } else if expr_type.has_flag(.result) {
251 g.writeln('), !${temp_var}.is_error) {')
252 } else {
253 g.writeln(')) {')
254 }
255 g.indent++
256 return SqlQueryDataGuardState{
257 temp_var: temp_var
258 expr_type: expr_type
259 }
260}
261
262fn (mut g Gen) emit_sql_query_data_guard_value_vars(cond ast.IfGuardExpr, state SqlQueryDataGuardState) {
263 if cond.vars.len == 0 || cond.vars.all(it.name == '_') {
264 return
265 }
266 base_type := g.base_type(state.expr_type)
267 if cond.vars.len == 1 {
268 if cond.vars[0].name == '_' {
269 return
270 }
271 left_var_name := c_name(cond.vars[0].name)
272 dot_or_ptr := if !state.expr_type.has_flag(.option_mut_param_t) {
273 '.'
274 } else {
275 '-> '
276 }
277 guard_typ := g.unwrap_generic(state.expr_type.clear_option_and_result())
278 guard_is_heap_obj := g.table.final_sym(guard_typ).is_heap() && !guard_typ.is_ptr()
279 if guard_is_heap_obj {
280 g.writeln('${base_type}* ${left_var_name} = (${base_type}*)${state.temp_var}${dot_or_ptr}data;')
281 } else if base_type.starts_with('Array_fixed') {
282 g.writeln('${base_type} ${left_var_name} = {0};')
283 g.writeln('memcpy(${left_var_name}, (${base_type}*)${state.temp_var}.data, sizeof(${base_type}));')
284 } else {
285 expr_sym := g.table.sym(state.expr_type)
286 if expr_sym.info is ast.FnType {
287 g.write_fntype_decl(left_var_name, expr_sym.info, state.expr_type.nr_muls())
288 if state.expr_type.nr_muls() == 0 {
289 g.writeln(' = *(${base_type}*)${state.temp_var}${dot_or_ptr}data;')
290 } else {
291 g.writeln(' = (${base_type}*)${state.temp_var}${dot_or_ptr}data;')
292 }
293 } else {
294 g.writeln('${base_type} ${left_var_name} = *(${base_type}*)${state.temp_var}${dot_or_ptr}data;')
295 }
296 }
297 return
298 }
299 sym := g.table.sym(state.expr_type)
300 if sym.info is ast.MultiReturn && sym.info.types.len == cond.vars.len {
301 for vi, var in cond.vars {
302 if var.name == '_' {
303 continue
304 }
305 var_typ := g.styp(sym.info.types[vi])
306 left_var_name := c_name(var.name)
307 g.writeln('${var_typ} ${left_var_name} = (*(${base_type}*)${state.temp_var}.data).arg${vi};')
308 }
309 }
310}
311
312fn (mut g Gen) emit_sql_query_data_guard_else_vars(state SqlQueryDataGuardState) {
313 if state.expr_type.has_flag(.result) {
314 g.writeln('IError err = ${state.temp_var}.err;')
315 }
316}
317
318fn (g &Gen) sql_query_data_guard_var_types(cond ast.IfGuardExpr, state SqlQueryDataGuardState) map[int]ast.Type {
319 mut guard_var_types := map[int]ast.Type{}
320 if state.expr_type == 0 {
321 return guard_var_types
322 }
323 sym := g.table.sym(state.expr_type)
324 if sym.kind == .multi_return {
325 mr_info := sym.info as ast.MultiReturn
326 if mr_info.types.len == cond.vars.len {
327 for vi, var in cond.vars {
328 if var.name == '_' {
329 continue
330 }
331 guard_var_types[int(var.pos.pos)] = mr_info.types[vi]
332 }
333 }
334 } else {
335 unwrapped_type := state.expr_type.clear_option_and_result()
336 for var in cond.vars {
337 if var.name == '_' {
338 continue
339 }
340 guard_var_types[int(var.pos.pos)] = unwrapped_type
341 }
342 }
343 return guard_var_types
344}
345
346fn (g &Gen) annotate_sql_query_data_guard_expr(mut expr ast.Expr, guard_var_types map[int]ast.Type) {
347 match mut expr {
348 ast.ArrayInit {
349 for mut item in expr.exprs {
350 g.annotate_sql_query_data_guard_expr(mut item, guard_var_types)
351 }
352 if expr.has_cap {
353 g.annotate_sql_query_data_guard_expr(mut expr.cap_expr, guard_var_types)
354 }
355 if expr.has_len {
356 g.annotate_sql_query_data_guard_expr(mut expr.len_expr, guard_var_types)
357 }
358 if expr.has_init {
359 g.annotate_sql_query_data_guard_expr(mut expr.init_expr, guard_var_types)
360 }
361 }
362 ast.CallExpr {
363 g.annotate_sql_query_data_guard_expr(mut expr.left, guard_var_types)
364 for mut arg in expr.args {
365 g.annotate_sql_query_data_guard_expr(mut arg.expr, guard_var_types)
366 }
367 }
368 ast.Ident {
369 if mut expr.info is ast.IdentVar {
370 if expr.obj is ast.Var {
371 if typ := guard_var_types[int(expr.obj.pos.pos)] {
372 expr.info.typ = typ
373 expr.obj.typ = typ
374 }
375 }
376 }
377 }
378 ast.IfExpr {
379 g.annotate_sql_query_data_guard_expr(mut expr.left, guard_var_types)
380 for mut branch in expr.branches {
381 g.annotate_sql_query_data_guard_expr(mut branch.cond, guard_var_types)
382 for mut stmt in branch.stmts {
383 if mut stmt is ast.ExprStmt {
384 g.annotate_sql_query_data_guard_expr(mut stmt.expr, guard_var_types)
385 }
386 }
387 }
388 }
389 ast.InfixExpr {
390 g.annotate_sql_query_data_guard_expr(mut expr.left, guard_var_types)
391 g.annotate_sql_query_data_guard_expr(mut expr.right, guard_var_types)
392 }
393 ast.ParExpr {
394 g.annotate_sql_query_data_guard_expr(mut expr.expr, guard_var_types)
395 }
396 ast.PrefixExpr {
397 g.annotate_sql_query_data_guard_expr(mut expr.right, guard_var_types)
398 }
399 ast.SelectorExpr {
400 g.annotate_sql_query_data_guard_expr(mut expr.expr, guard_var_types)
401 }
402 ast.StringInterLiteral {
403 for mut item in expr.exprs {
404 g.annotate_sql_query_data_guard_expr(mut item, guard_var_types)
405 }
406 }
407 ast.StructInit {
408 for mut init_field in expr.init_fields {
409 g.annotate_sql_query_data_guard_expr(mut init_field.expr, guard_var_types)
410 }
411 }
412 else {}
413 }
414}
415
416fn (g &Gen) annotate_sql_query_data_guard_items(mut items []ast.SqlQueryDataItem, cond ast.IfGuardExpr, state SqlQueryDataGuardState) {
417 guard_var_types := g.sql_query_data_guard_var_types(cond, state)
418 if guard_var_types.len == 0 {
419 return
420 }
421 for mut item in items {
422 match mut item {
423 ast.SqlQueryDataLeaf {
424 g.annotate_sql_query_data_guard_expr(mut item.expr, guard_var_types)
425 }
426 ast.SqlQueryDataIf {
427 for mut branch in item.branches {
428 if branch.cond !is ast.EmptyExpr {
429 g.annotate_sql_query_data_guard_expr(mut branch.cond, guard_var_types)
430 }
431 g.annotate_sql_query_data_guard_items(mut branch.items, cond, state)
432 }
433 }
434 }
435 }
436}
437
438fn (g &Gen) orm_if_guard_ident_type(node ast.Ident, typ ast.Type) ast.Type {
439 if typ != 0 {
440 return typ
441 }
442 if node.obj is ast.Var {
443 if node.obj.expr is ast.IfGuardExpr {
444 if node.obj.expr.expr_type != 0 {
445 sym := g.table.sym(node.obj.expr.expr_type)
446 if sym.kind == .multi_return {
447 mr_info := sym.info as ast.MultiReturn
448 if mr_info.types.len == node.obj.expr.vars.len {
449 for vi, var in node.obj.expr.vars {
450 if var.name == node.name {
451 return mr_info.types[vi]
452 }
453 }
454 }
455 } else {
456 return node.obj.expr.expr_type.clear_option_and_result()
457 }
458 }
459 }
460 }
461 return typ
462}
463
464fn (mut g Gen) emit_sql_query_data_items(query_var string, items []ast.SqlQueryDataItem, resolve_columns bool) {
465 for item in items {
466 match item {
467 ast.SqlQueryDataLeaf {
468 g.emit_sql_query_data_leaf(query_var, item, resolve_columns)
469 }
470 ast.SqlQueryDataIf {
471 mut prev_guard := SqlQueryDataGuardState{}
472 mut has_prev_guard := false
473 for idx, branch in item.branches {
474 is_else := branch.cond is ast.EmptyExpr
475 if idx == 0 {
476 if is_else {
477 g.writeln('{')
478 g.indent++
479 if has_prev_guard {
480 g.emit_sql_query_data_guard_else_vars(prev_guard)
481 }
482 } else {
483 if branch.cond is ast.IfGuardExpr {
484 prev_guard = g.emit_sql_query_data_guard_open(branch.cond)
485 has_prev_guard = true
486 g.emit_sql_query_data_guard_value_vars(branch.cond, prev_guard)
487 } else {
488 g.write('if (')
489 g.expr(branch.cond)
490 g.writeln(') {')
491 g.indent++
492 has_prev_guard = false
493 }
494 }
495 } else {
496 if is_else {
497 g.writeln('else {')
498 g.indent++
499 if has_prev_guard {
500 g.emit_sql_query_data_guard_else_vars(prev_guard)
501 }
502 } else if branch.cond is ast.IfGuardExpr {
503 g.write('else ')
504 prev_guard = g.emit_sql_query_data_guard_open(branch.cond)
505 has_prev_guard = true
506 g.emit_sql_query_data_guard_value_vars(branch.cond, prev_guard)
507 } else {
508 g.write('else if (')
509 g.expr(branch.cond)
510 g.writeln(') {')
511 g.indent++
512 has_prev_guard = false
513 }
514 }
515 mut branch_items := branch.items.clone()
516 if branch.cond is ast.IfGuardExpr {
517 g.annotate_sql_query_data_guard_items(mut branch_items, branch.cond,
518 prev_guard)
519 }
520 g.emit_sql_query_data_items(query_var, branch_items, resolve_columns)
521 g.indent--
522 g.writeln('}')
523 }
524 }
525 }
526 }
527}
528
529fn (mut g Gen) emit_sql_query_data_leaf(query_var string, node ast.SqlQueryDataLeaf, resolve_columns bool) {
530 mut expr := node.expr
531 expr_ := expr.remove_par()
532 if expr_ is ast.InfixExpr {
533 mut field_name := sql_query_data_field_name(expr_.left)
534 if resolve_columns {
535 field := g.get_orm_current_table_field(field_name) or {
536 verror('field "${field_name}" does not exist on "${g.sql_table_name}"')
537 }
538 field_name = g.get_orm_column_name_from_struct_field(field)
539 }
540 is_nil_comparison := expr_.right is ast.Nil && expr_.op in [.eq, .ne]
541 ignore_rhs := expr_.op in [.key_is, .not_is] || is_nil_comparison
542 g.writeln('if (${query_var}.fields.len > 0) {')
543 g.indent++
544 g.writeln('builtin__array_push(&${query_var}.is_and, _MOV((bool[1]){ true }));')
545 g.indent--
546 g.writeln('}')
547 g.writeln('builtin__array_push(&${query_var}.fields, _MOV((string[1]){ _S("${field_name}") }));')
548 g.writeln('builtin__array_push(&${query_var}.kinds, _MOV((orm__OperationKind[1]){ ${sql_query_data_op_kind(expr_)} }));')
549 if !ignore_rhs {
550 g.write('builtin__array_push(&${query_var}.data, _MOV((orm__Primitive[1]){')
551 g.write_orm_expr_to_primitive(expr_.right)
552 g.writeln('}));')
553 }
554 } else {
555 verror('ORM: dynamic query-data leaf must be an infix expression')
556 }
557}
558
559// All databases that are supported by ORM
560// must implement the `Connection` interface from the file `vlib/orm/orm.v`
561// The main function of the cgen for ORM is to identify the current database type,
562// generate the necessary parameters for invoking its methods,
563// and correctly process the results, saving them to a variable that the user can access.
564// Essentially, it involves a desugaring of the ORM syntax.
565// For example, if you write:
566// ```v
567// sql db {
568// select from User
569// }
570// ```
571// cgen will write calling the function `select` of the needed database.
572// If you use sqlite, it calls `select` from `vlib/db/sqlite/orm.v`
573
574// sql_select_expr writes C code that calls ORM functions for selecting objects
575// from the database, which is used by the `select` query.
576fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
577 // users :=
578 left := g.go_before_last_stmt()
579 connection_var_name := g.new_tmp_var()
580
581 g.writeln('')
582 g.write_orm_connection_init(connection_var_name, &node.db_expr)
583 result_var := g.new_tmp_var()
584 result_c_typ := g.styp(node.typ)
585 g.writeln('${result_c_typ} ${result_var};')
586 g.write_orm_select(node, connection_var_name, result_var)
587 unwrapped_c_typ := g.styp(node.typ.clear_flag(.result))
588 g.write('${left} *(${unwrapped_c_typ}*)${result_var}.data')
589}
590
591fn (mut g Gen) sql_insert_expr(node ast.SqlExpr) {
592 left := g.go_before_last_stmt()
593 g.writeln('')
594 connection_var_name := g.new_tmp_var()
595 g.write_orm_connection_init(connection_var_name, &node.db_expr)
596 table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
597 table_attrs := g.get_table_attrs_by_struct_type(node.table_expr.typ)
598 result_var_name := g.new_tmp_var()
599 g.sql_table_name = g.table.sym(node.table_expr.typ).name
600 g.sql_table_typ = node.table_expr.typ
601
602 // orm_insert needs an SqlStmtLine, build it from SqlExpr (most nodes are the same)
603 hack_stmt_line := g.build_sql_stmt_line_from_sql_expr(node)
604 g.write_orm_insert(hack_stmt_line, table_name, connection_var_name, result_var_name,
605 node.or_expr, table_attrs)
606
607 g.write2(left,
608 'orm__Connection_name_table[${connection_var_name}._typ]._method_last_id(${connection_var_name}._object)')
609}
610
611fn (mut g Gen) build_sql_stmt_line_from_sql_expr(node ast.SqlExpr) ast.SqlStmtLine {
612 mut sub_structs := map[string]ast.SqlStmtLine{}
613 for key, sub in node.sub_structs {
614 sub_structs[key] = g.build_sql_stmt_line_from_sql_expr(sub)
615 }
616 return ast.SqlStmtLine{
617 object_var: node.inserted_var
618 fields: node.fields
619 table_expr: node.table_expr
620 sub_structs: sub_structs
621 scope: node.scope
622 }
623}
624
625// sql_stmt writes C code that calls ORM functions for
626// performing various database operations such as creating and dropping tables,
627// as well as inserting and updating objects.
628// Can contain several queries. For example:
629// ```v
630// sql db {
631// create table User
632// insert user into User
633// }!
634// ```
635// NOTE: Currently, in ORM only the `select` query is an expression.
636// The others are statements.
637fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
638 connection_var_name := g.new_tmp_var()
639
640 g.write_orm_connection_init(connection_var_name, &node.db_expr)
641
642 for line in node.lines {
643 g.sql_stmt_line(line, connection_var_name, node.or_expr)
644 }
645}
646
647// sql_stmt_line writes C code that calls ORM functions for
648// performing various database operations such as creating and dropping tables,
649// as well as inserting and updating objects.
650// It is part of a multi-line query. For example, `create table User`
651fn (mut g Gen) sql_stmt_line(stmt_line ast.SqlStmtLine, connection_var_name string, or_expr ast.OrExpr) {
652 g.sql_last_stmt_out_len = g.out.len
653 mut node := stmt_line
654 table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
655 table_attrs := g.get_table_attrs_by_struct_type(node.table_expr.typ)
656 result_var_name := g.new_tmp_var()
657 g.sql_table_name = g.table.sym(node.table_expr.typ).name
658 g.sql_table_typ = node.table_expr.typ
659
660 if node.kind != .create {
661 node.fields = g.filter_struct_fields_by_orm_attrs(node.fields)
662 }
663
664 if node.kind == .create {
665 g.write_orm_create_table(node, table_name, connection_var_name, result_var_name,
666 table_attrs)
667 } else if node.kind == .drop {
668 g.write_orm_drop_table(node, table_name, connection_var_name, result_var_name, table_attrs)
669 } else if node.kind == .insert {
670 g.write_orm_insert(node, table_name, connection_var_name, result_var_name, or_expr,
671 table_attrs)
672 } else if node.kind == .upsert {
673 g.write_orm_upsert(node, table_name, connection_var_name, result_var_name, or_expr,
674 table_attrs)
675 } else if node.kind == .update {
676 g.write_orm_update(node, table_name, connection_var_name, result_var_name, table_attrs)
677 } else if node.kind == .delete {
678 g.write_orm_delete(node, table_name, connection_var_name, result_var_name, table_attrs)
679 }
680
681 g.or_block(result_var_name, or_expr, ast.int_type.set_flag(.result))
682}
683
684// write_orm_connection_init writes C code that saves the database connection
685// into a variable for later use in ORM queries.
686fn (mut g Gen) write_orm_connection_init(connection_var_name string, db_expr &ast.Expr) {
687 db_expr_type := g.get_db_expr_type(db_expr) or { verror('ORM: unknown db type for ${db_expr}') }
688
689 mut db_ctype_name := g.styp(db_expr_type.clear_flag(.shared_f))
690 is_pointer := db_ctype_name.ends_with('*')
691 reference_sign := if is_pointer { '' } else { '&' }
692 db_ctype_name = db_ctype_name.trim_right('*')
693
694 g.writeln('// ORM')
695 g.write('orm__Connection ${connection_var_name} = ')
696
697 if db_ctype_name == 'orm__Connection' {
698 g.expr(db_expr)
699 g.writeln(';')
700 } else {
701 g.write('(orm__Connection){._${db_ctype_name} = ${reference_sign}')
702 if db_expr_type.has_flag(.shared_f) {
703 g.write('&')
704 }
705 g.expr(db_expr)
706 if db_expr_type.has_flag(.shared_f) {
707 g.write('->val')
708 }
709 g.writeln(', ._typ = _orm__Connection_${db_ctype_name}_index};')
710 }
711}
712
713// write_orm_table_struct writes C code for the orm.Table struct
714fn (mut g Gen) write_orm_table_struct(typ ast.Type) {
715 table_name := g.get_table_name_by_struct_type(typ)
716 table_attrs := g.get_table_attrs_by_struct_type(typ)
717
718 g.writeln('((orm__Table){')
719 g.indent++
720 g.writeln('.name = _S("${table_name}"),')
721 g.writeln('.attrs = builtin__new_array_from_c_array(${table_attrs.len}, ${table_attrs.len}, sizeof(VAttribute),')
722 g.indent++
723
724 if table_attrs.len > 0 {
725 g.write('_MOV((VAttribute[${table_attrs.len}]){')
726 g.indent++
727 for attr in table_attrs {
728 g.write('(VAttribute){')
729 g.indent++
730 name1 := util.smart_quote(attr.name, false)
731 name := cescape_nonascii(name1)
732 g.write(' .name = _S("${name}"),')
733 g.write(' .has_arg = ${attr.has_arg},')
734 arg1 := util.smart_quote(attr.arg, false)
735 arg := cescape_nonascii(arg1)
736 g.write(' .arg = _S("${arg}"),')
737 g.write(' .kind = ${int(attr.kind)},')
738 g.indent--
739 g.write('},')
740 }
741 g.indent--
742 g.writeln('})')
743 } else {
744 g.writeln('NULL // No attrs')
745 }
746 g.indent--
747 g.writeln(')')
748 g.indent--
749 g.write('})')
750}
751
752// write_orm_create_table writes C code that calls ORM functions for creating tables.
753fn (mut g Gen) write_orm_create_table(node ast.SqlStmtLine, table_name string, connection_var_name string,
754 result_var_name string, _ []ast.Attr) {
755 g.writeln('// sql { create table `${table_name}` }')
756 g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_create(')
757 g.indent++
758 g.writeln('${connection_var_name}._object, // Connection object')
759 g.write_orm_table_struct(node.table_expr.typ)
760 g.writeln(',')
761 g.writeln('builtin__new_array_from_c_array(${node.fields.len}, ${node.fields.len}, sizeof(orm__TableField),')
762 g.indent++
763
764 if node.fields.len > 0 {
765 g.writeln('_MOV((orm__TableField[${node.fields.len}]){')
766 g.indent++
767
768 for field in node.fields {
769 g.writeln('// `${table_name}`.`${field.name}`')
770 // Safety check: ensure field type is valid (not 0 and not still generic)
771 if field.typ == 0 || field.typ.has_flag(.generic) {
772 verror('ORM: field `${field.name}` in table `${table_name}` has unresolved type - this may be due to using a generic struct that was not properly instantiated')
773 }
774 final_field_typ := g.table.final_type(field.typ)
775 sym := g.table.sym(final_field_typ)
776 typ := match true {
777 sym.name == 'time.Time' { '_const_orm__time_' }
778 sym.kind == .enum { '_const_orm__enum_' }
779 else { final_field_typ.idx().str() }
780 }
781
782 g.writeln('(orm__TableField){')
783 g.indent++
784 g.writeln('.name = _S("${field.name}"),')
785 g.writeln('.typ = ${typ}, // `${sym.name}`')
786 g.writeln('.is_arr = ${sym.kind == .array}, ')
787 g.writeln('.nullable = ${final_field_typ.has_flag(.option)},')
788 g.writeln('.default_val = (string){ .str = (byteptr) "${field.default_val}", .is_lit = 1 },')
789 g.writeln('.attrs = builtin__new_array_from_c_array(${field.attrs.len}, ${field.attrs.len}, sizeof(VAttribute),')
790 g.indent++
791
792 if field.attrs.len > 0 {
793 g.write('_MOV((VAttribute[${field.attrs.len}]){')
794 g.indent++
795 for attr in field.attrs {
796 g.write('(VAttribute){')
797 g.indent++
798 name1 := util.smart_quote(attr.name, false)
799 name := cescape_nonascii(name1)
800 g.write(' .name = _S("${name}"),')
801 g.write(' .has_arg = ${attr.has_arg},')
802 arg1 := util.smart_quote(attr.arg, false)
803 arg := cescape_nonascii(arg1)
804 g.write(' .arg = _S("${arg}"),')
805 g.write(' .kind = ${int(attr.kind)},')
806 g.indent--
807 g.write('},')
808 }
809 g.indent--
810 g.writeln('})')
811 } else {
812 g.writeln('NULL // No attrs')
813 }
814 g.indent--
815 g.writeln(')')
816 g.indent--
817 g.writeln('},')
818 }
819
820 g.indent--
821 g.writeln('})')
822 } else {
823 g.writeln('NULL')
824 }
825
826 g.indent--
827 g.writeln(')')
828 g.indent--
829 g.writeln(');')
830}
831
832// write_orm_drop_table writes C code that calls ORM functions for dropping tables.
833fn (mut g Gen) write_orm_drop_table(node ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, _ []ast.Attr) {
834 g.writeln('// sql { drop table `${table_name}` }')
835 g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_drop(')
836 g.indent++
837 g.writeln('${connection_var_name}._object, // Connection object')
838 g.write_orm_table_struct(node.table_expr.typ)
839 g.indent--
840 g.writeln(');')
841}
842
843// write_orm_insert writes C code that calls ORM functions for inserting structs into a table.
844fn (mut g Gen) write_orm_insert(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string,
845 or_expr &ast.OrExpr, _ []ast.Attr) {
846 if node.is_array_insert {
847 g.write_orm_bulk_insert(node, table_name, connection_var_name, result_var_name, or_expr)
848 return
849 }
850 last_ids_variable_name := g.new_tmp_var()
851
852 g.writeln('Array_orm__Primitive ${last_ids_variable_name} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
853 g.write_orm_insert_with_last_ids(node, connection_var_name, table_name, last_ids_variable_name,
854 result_var_name, '', '', or_expr)
855}
856
857fn (mut g Gen) write_orm_bulk_insert(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, or_expr &ast.OrExpr) {
858 fields := g.orm_non_array_fields(node.fields)
859 auto_fields := get_auto_field_idxs(fields)
860 row_type := g.styp(node.table_expr.typ)
861 row_var := g.new_tmp_var()
862 idx_var := g.new_tmp_var()
863 data_var := g.new_tmp_var()
864 g.writeln('${result_name}_void ${result_var_name};')
865 g.writeln('if (${node.object_var}.len == 0) {')
866 g.indent++
867 g.writeln('${result_var_name} = (${result_name}_void){0};')
868 g.indent--
869 g.writeln('} else {')
870 g.indent++
871 if auto_fields.len > 0 {
872 g.writeln('${result_var_name} = (${result_name}_void){0};')
873 g.writeln('for (${ast.int_type_name} ${idx_var} = 0; ${idx_var} < ${node.object_var}.len; ${idx_var}++) {')
874 g.indent++
875 g.writeln('${row_type} ${row_var} = (*(${row_type}*)builtin__array_get(${node.object_var}, ${idx_var}));')
876 row_result_var := g.new_tmp_var()
877 mut row_node := *node
878 row_node.object_var = row_var
879 row_node.is_array_insert = false
880 g.write_orm_insert(&row_node, table_name, connection_var_name, row_result_var, or_expr,
881 []ast.Attr{})
882 g.or_block(row_result_var, *or_expr, ast.int_type.set_flag(.result))
883 g.writeln('${result_var_name} = ${row_result_var};')
884 g.indent--
885 g.writeln('}')
886 g.indent--
887 g.writeln('}')
888 return
889 }
890 g.writeln('Array_orm__Primitive ${data_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
891 g.writeln('for (${ast.int_type_name} ${idx_var} = 0; ${idx_var} < ${node.object_var}.len; ${idx_var}++) {')
892 g.indent++
893 g.writeln('${row_type} ${row_var} = (*(${row_type}*)builtin__array_get(${node.object_var}, ${idx_var}));')
894 for field in fields {
895 g.write('builtin__array_push(&${data_var}, _MOV((orm__Primitive[1]){')
896 g.write_orm_field_access_to_primitive(field, row_var, node.table_expr.typ,
897 g.table.sym(node.table_expr.typ))
898 g.writeln('}));')
899 }
900 g.indent--
901 g.writeln('}')
902 g.writeln('// sql { insert into `${table_name}` }')
903 g.writeln('${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_insert(')
904 g.indent++
905 g.writeln('${connection_var_name}._object, // Connection object')
906 g.write_orm_table_struct(node.table_expr.typ)
907 g.writeln(',')
908 g.writeln('(orm__QueryData){')
909 g.indent++
910 g.writeln('.fields = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),')
911 g.indent++
912 if fields.len > 0 {
913 g.writeln('_MOV((string[${fields.len}]){')
914 g.indent++
915 for f in fields {
916 g.writeln('_S("${g.get_orm_column_name_from_struct_field(f)}"),')
917 }
918 g.indent--
919 g.writeln('})')
920 } else {
921 g.writeln('NULL')
922 }
923 g.indent--
924 g.writeln('),')
925 g.writeln('.data = ${data_var},')
926 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
927 if auto_fields.len > 0 {
928 g.writeln('.auto_fields = builtin__new_array_from_c_array(${auto_fields.len}, ${auto_fields.len}, sizeof(${ast.int_type_name}),')
929 g.indent++
930 g.write('_MOV((${ast.int_type_name}[${auto_fields.len}]){')
931 for i in auto_fields {
932 g.write(' ${i},')
933 }
934 g.writeln(' })),')
935 g.indent--
936 } else {
937 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
938 }
939 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
940 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
941 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
942 g.writeln('.batch_rows = ${node.object_var}.len,')
943 g.indent--
944 g.writeln('}')
945 g.indent--
946 g.writeln(');')
947 g.indent--
948 g.writeln('}')
949}
950
951fn (mut g Gen) write_orm_upsert(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string,
952 _ ast.OrExpr, table_attrs []ast.Attr) {
953 g.writeln('// sql { upsert into `${table_name}` }')
954 fields := node.fields
955 auto_fields := get_auto_field_idxs(fields)
956 mut inserting_object_type := ast.void_type
957 mut member_access_type := '.'
958 if node.scope != unsafe { nil } {
959 if inserting_object := node.scope.find(node.object_var) {
960 if inserting_object.typ.is_ptr() {
961 member_access_type = '->'
962 }
963 inserting_object_type = g.orm_inserting_object_type(inserting_object)
964 } else {
965 inserting_object_type = node.table_expr.typ
966 }
967 }
968 inserting_object_sym := g.table.sym(inserting_object_type)
969 data_var_name := g.new_tmp_var()
970 g.writeln('orm__QueryData ${data_var_name} = (orm__QueryData){')
971 g.indent++
972 if fields.len > 0 {
973 g.writeln('.fields = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),')
974 g.indent++
975 g.writeln('_MOV((string[${fields.len}]){')
976 g.indent++
977 for field in fields {
978 g.writeln('_S("${g.get_orm_column_name_from_struct_field(field)}"),')
979 }
980 g.indent--
981 g.writeln('})')
982 g.indent--
983 g.writeln('),')
984 g.writeln('.data = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(orm__Primitive),')
985 g.indent++
986 g.writeln('_MOV((orm__Primitive[${fields.len}]){')
987 g.indent++
988 for field in fields {
989 final_field_typ := g.table.final_type(field.typ)
990 sym := g.table.sym(final_field_typ)
991 mut typ := g.orm_primitive_field_name(field.typ)
992 mut ctyp := sym.cname
993 typ = vint2int(typ)
994 var := '${node.object_var}${member_access_type}${orm_field_access_name(field.name)}'
995 if final_field_typ.has_flag(.option) {
996 g.writeln('${var}.state == 2 ? _const_orm__null_primitive : orm__${typ}_to_primitive(*(${ctyp}*)(${var}.data)),')
997 } else if inserting_object_sym.kind == .sum_type {
998 table_sym := g.table.sym(node.table_expr.typ)
999 sum_type_var := '(*${node.object_var}._${table_sym.cname})${member_access_type}${orm_field_access_name(field.name)}'
1000 g.writeln('orm__${typ}_to_primitive(${sum_type_var}),')
1001 } else {
1002 g.writeln('orm__${typ}_to_primitive(${var}),')
1003 }
1004 }
1005 g.indent--
1006 g.writeln('})')
1007 g.indent--
1008 g.writeln('),')
1009 } else {
1010 g.writeln('.fields = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
1011 g.writeln('.data = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0),')
1012 }
1013 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1014 if auto_fields.len > 0 {
1015 g.writeln('.auto_fields = builtin__new_array_from_c_array(${auto_fields.len}, ${auto_fields.len}, sizeof(${ast.int_type_name}),')
1016 g.indent++
1017 g.write('_MOV((${ast.int_type_name}[${auto_fields.len}]){')
1018 for i in auto_fields {
1019 g.write(' ${i},')
1020 }
1021 g.writeln(' })),')
1022 g.indent--
1023 } else {
1024 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1025 }
1026 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
1027 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
1028 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
1029 g.indent--
1030 g.writeln('};')
1031 conflict_groups := g.get_orm_upsert_conflict_groups(fields, table_attrs)
1032 conflict_groups_var_name := g.new_tmp_var()
1033 g.write('Array_Array_string ${conflict_groups_var_name} = ')
1034 g.write_orm_upsert_conflict_groups(conflict_groups)
1035 g.writeln(';')
1036 prepared_var_name := g.new_tmp_var()
1037 g.writeln('orm__UpsertData ${prepared_var_name} = orm__prepare_upsert(${data_var_name}, ${conflict_groups_var_name});')
1038 g.writeln('${result_name}_void ${result_var_name};')
1039 g.writeln('if (!${prepared_var_name}.valid) {')
1040 g.indent++
1041 g.write('${result_var_name} = orm__upsert_missing_conflict_error(')
1042 g.write_orm_table_struct(node.table_expr.typ)
1043 g.writeln(');')
1044 g.indent--
1045 g.writeln('} else {')
1046 g.indent++
1047 select_result_var_name := g.new_tmp_var()
1048 g.writeln('${result_name}_Array_Array_orm__Primitive ${select_result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_select(')
1049 g.indent++
1050 g.writeln('${connection_var_name}._object, // Connection object')
1051 g.writeln('(orm__SelectConfig){')
1052 g.indent++
1053 g.write('.table = ')
1054 g.write_orm_table_struct(node.table_expr.typ)
1055 g.writeln(',')
1056 g.writeln('.aggregate_kind = orm__AggregateKind__count,')
1057 g.writeln('.aggregate_field = _S(""),')
1058 g.writeln('.has_where = true,')
1059 g.writeln('.has_order = false,')
1060 g.writeln('.order = _S(""),')
1061 g.writeln('.order_type = orm__OrderType__asc,')
1062 g.writeln('.has_limit = false,')
1063 g.writeln('.primary = _S("id"),')
1064 g.writeln('.has_offset = false,')
1065 g.writeln('.has_distinct = false,')
1066 g.writeln('.fields = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
1067 g.writeln('.select_exprs = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
1068 g.writeln('.types = builtin__new_array_from_c_array(1, 1, sizeof(${ast.int_type_name}), _MOV((${ast.int_type_name}[1]){ ${ast.int_type.idx()}, })),')
1069 g.writeln('.joins = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__JoinConfig), 0),')
1070 g.indent--
1071 g.writeln('},')
1072 g.writeln('(orm__QueryData){')
1073 g.indent++
1074 g.writeln('.fields = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
1075 g.writeln('.data = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0),')
1076 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1077 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1078 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
1079 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
1080 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
1081 g.indent--
1082 g.writeln('},')
1083 g.writeln('${prepared_var_name}.where')
1084 g.indent--
1085 g.writeln(');')
1086 g.writeln('${result_var_name}.is_error = ${select_result_var_name}.is_error;')
1087 g.writeln('${result_var_name}.err = ${select_result_var_name}.err;')
1088 g.writeln('if (!${result_var_name}.is_error) {')
1089 g.indent++
1090 count_rows_var_name := g.new_tmp_var()
1091 count_var_name := g.new_tmp_var()
1092 g.writeln('Array_Array_orm__Primitive ${count_rows_var_name} = (*(Array_Array_orm__Primitive*)${select_result_var_name}.data);')
1093 g.writeln('${ast.int_type_name} ${count_var_name} = orm__upsert_count(${count_rows_var_name});')
1094 g.writeln('if (${count_var_name} == 0) {')
1095 g.indent++
1096 g.writeln('${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_insert(')
1097 g.indent++
1098 g.writeln('${connection_var_name}._object, // Connection object')
1099 g.write_orm_table_struct(node.table_expr.typ)
1100 g.writeln(',')
1101 g.writeln('${data_var_name}')
1102 g.indent--
1103 g.writeln(');')
1104 g.indent--
1105 g.writeln('} else if (${count_var_name} == 1) {')
1106 g.indent++
1107 g.writeln('if (${prepared_var_name}.insert_data.fields.len == 0) {')
1108 g.indent++
1109 g.writeln('${result_var_name} = (${result_name}_void){0};')
1110 g.indent--
1111 g.writeln('} else {')
1112 g.indent++
1113 g.writeln('${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_update(')
1114 g.indent++
1115 g.writeln('${connection_var_name}._object, // Connection object')
1116 g.write_orm_table_struct(node.table_expr.typ)
1117 g.writeln(',')
1118 g.writeln('${prepared_var_name}.insert_data,')
1119 g.writeln('${prepared_var_name}.where')
1120 g.indent--
1121 g.writeln(');')
1122 g.indent--
1123 g.writeln('}')
1124 g.indent--
1125 g.writeln('} else {')
1126 g.indent++
1127 g.write('${result_var_name} = orm__upsert_ambiguous_error(')
1128 g.write_orm_table_struct(node.table_expr.typ)
1129 g.writeln(');')
1130 g.indent--
1131 g.writeln('}')
1132 g.indent--
1133 g.writeln('}')
1134 g.indent--
1135 g.writeln('}')
1136}
1137
1138// write_orm_update writes C code that calls ORM functions for updating rows.
1139fn (mut g Gen) write_orm_update(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, _ []ast.Attr) {
1140 mut dynamic_update_data_var := ''
1141 if node.is_dynamic {
1142 dynamic_update_data_var = g.emit_dynamic_sql_query_data(node.update_data_expr)
1143 }
1144 if node.is_array_update {
1145 g.write_orm_bulk_update(node, table_name, connection_var_name, result_var_name)
1146 return
1147 }
1148 g.writeln('// sql { update `${table_name}` }')
1149 g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_update(')
1150 g.indent++
1151 g.writeln('${connection_var_name}._object, // Connection object')
1152 g.write_orm_table_struct(node.table_expr.typ)
1153 g.writeln(',')
1154 if node.is_dynamic {
1155 g.writeln('${dynamic_update_data_var},')
1156 } else {
1157 g.writeln('(orm__QueryData){')
1158 g.indent++
1159 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
1160 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
1161 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1162 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
1163
1164 if node.updated_columns.len > 0 {
1165 g.writeln('.fields = builtin__new_array_from_c_array(${node.updated_columns.len}, ${node.updated_columns.len}, sizeof(string),')
1166 g.indent++
1167 g.writeln('_MOV((string[${node.updated_columns.len}]){')
1168 g.indent++
1169 for field in node.updated_columns {
1170 g.writeln('_S("${field}"),')
1171 }
1172 g.indent--
1173 g.writeln('})')
1174 g.indent--
1175 } else {
1176 g.writeln('.fields = builtin____new_array_with_default_noscan(${node.updated_columns.len}, ${node.updated_columns.len}, sizeof(string), 0')
1177 }
1178
1179 g.writeln2('),',
1180 '.data = builtin__new_array_from_c_array(${node.update_exprs.len}, ${node.update_exprs.len}, sizeof(orm__Primitive),')
1181
1182 if node.update_exprs.len > 0 {
1183 g.indent++
1184 g.writeln('_MOV((orm__Primitive[${node.update_exprs.len}]){')
1185 g.indent++
1186 for i, e in node.update_exprs {
1187 column := node.updated_columns[i]
1188 if field := g.get_orm_field_by_column_name(node.fields, column) {
1189 final_field_typ := g.table.final_type(field.typ.clear_flag(.option))
1190 sym := g.table.sym(final_field_typ)
1191 if sym.kind == .struct && sym.name != 'time.Time' {
1192 g.write_orm_struct_field_expr_to_primitive(field, e)
1193 continue
1194 }
1195 }
1196 g.write_orm_expr_to_primitive(e)
1197 }
1198 g.indent--
1199 g.writeln('})')
1200 g.indent--
1201 }
1202
1203 g.writeln('),')
1204 g.indent--
1205 g.writeln('},')
1206 }
1207 g.write_orm_where(node.where_expr)
1208 g.indent--
1209 g.writeln(');')
1210}
1211
1212fn (mut g Gen) write_orm_bulk_update(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string) {
1213 row_type := g.styp(node.table_expr.typ)
1214 row_var := g.new_tmp_var()
1215 idx_var := g.new_tmp_var()
1216 data_var := g.new_tmp_var()
1217 where_data_var := g.new_tmp_var()
1218 where_fields_var := g.new_tmp_var()
1219 where_kinds_var := g.new_tmp_var()
1220 where_is_and_var := g.new_tmp_var()
1221 key_field := g.get_orm_field_by_column_name(node.fields, node.array_update_key) or {
1222 verror('ORM: field "${node.array_update_key}" does not exist on "${g.sql_table_name}"')
1223 }
1224 g.writeln('${result_name}_void ${result_var_name};')
1225 g.writeln('if (${node.array_update_var}.len == 0) {')
1226 g.indent++
1227 g.writeln('${result_var_name} = (${result_name}_void){0};')
1228 g.indent--
1229 g.writeln('} else {')
1230 g.indent++
1231 g.writeln('Array_orm__Primitive ${data_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
1232 g.writeln('Array_orm__Primitive ${where_data_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
1233 g.writeln('Array_string ${where_fields_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0);')
1234 g.writeln('Array_orm__OperationKind ${where_kinds_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0);')
1235 g.writeln('Array_bool ${where_is_and_var} = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0);')
1236 g.writeln('for (${ast.int_type_name} ${idx_var} = 0; ${idx_var} < ${node.array_update_var}.len; ${idx_var}++) {')
1237 g.indent++
1238 g.writeln('${row_type} ${row_var} = (*(${row_type}*)builtin__array_get(${node.array_update_var}, ${idx_var}));')
1239 g.writeln('builtin__array_push(&${where_fields_var}, _MOV((string[1]){ _S("${node.array_update_key}") }));')
1240 g.writeln('builtin__array_push(&${where_kinds_var}, _MOV((orm__OperationKind[1]){ orm__OperationKind__eq }));')
1241 g.writeln('if (${idx_var} > 0) {')
1242 g.indent++
1243 g.writeln('builtin__array_push(&${where_is_and_var}, _MOV((bool[1]){ false }));')
1244 g.indent--
1245 g.writeln('}')
1246 g.write('builtin__array_push(&${where_data_var}, _MOV((orm__Primitive[1]){')
1247 g.write_orm_field_access_to_primitive(key_field, row_var, node.table_expr.typ,
1248 g.table.sym(node.table_expr.typ))
1249 g.writeln('}));')
1250 g.indent--
1251 g.writeln('}')
1252 for expr in node.update_exprs {
1253 selector := expr as ast.SelectorExpr
1254 value_field := get_orm_field_by_struct_field_name(node.fields, selector.field_name) or {
1255 verror('ORM: field "${selector.field_name}" does not exist on "${g.sql_table_name}"')
1256 }
1257 g.writeln('for (${ast.int_type_name} ${idx_var} = 0; ${idx_var} < ${node.array_update_var}.len; ${idx_var}++) {')
1258 g.indent++
1259 g.writeln('${row_type} ${row_var} = (*(${row_type}*)builtin__array_get(${node.array_update_var}, ${idx_var}));')
1260 g.write('builtin__array_push(&${data_var}, _MOV((orm__Primitive[1]){')
1261 g.write_orm_field_access_to_primitive(key_field, row_var, node.table_expr.typ,
1262 g.table.sym(node.table_expr.typ))
1263 g.writeln('}));')
1264 g.write('builtin__array_push(&${data_var}, _MOV((orm__Primitive[1]){')
1265 g.write_orm_field_access_to_primitive(value_field, row_var, node.table_expr.typ,
1266 g.table.sym(node.table_expr.typ))
1267 g.writeln('}));')
1268 g.indent--
1269 g.writeln('}')
1270 }
1271 g.writeln('// sql { update `${table_name}` }')
1272 g.writeln('${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_update(')
1273 g.indent++
1274 g.writeln('${connection_var_name}._object, // Connection object')
1275 g.write_orm_table_struct(node.table_expr.typ)
1276 g.writeln(',')
1277 g.writeln('(orm__QueryData){')
1278 g.indent++
1279 g.writeln('.fields = builtin__new_array_from_c_array(${node.updated_columns.len}, ${node.updated_columns.len}, sizeof(string),')
1280 g.indent++
1281 g.writeln('_MOV((string[${node.updated_columns.len}]){')
1282 g.indent++
1283 for field in node.updated_columns {
1284 g.writeln('_S("${field}"),')
1285 }
1286 g.indent--
1287 g.writeln('})')
1288 g.indent--
1289 g.writeln('),')
1290 g.writeln('.data = ${data_var},')
1291 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1292 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1293 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
1294 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
1295 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
1296 g.writeln('.batch_rows = ${node.array_update_var}.len,')
1297 g.writeln('.batch_key = _S("${node.array_update_key}"),')
1298 g.indent--
1299 g.writeln('},')
1300 g.writeln('(orm__QueryData){')
1301 g.indent++
1302 g.writeln('.fields = ${where_fields_var},')
1303 g.writeln('.data = ${where_data_var},')
1304 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1305 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1306 g.writeln('.kinds = ${where_kinds_var},')
1307 g.writeln('.is_and = ${where_is_and_var},')
1308 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
1309 g.indent--
1310 g.writeln('}')
1311 g.indent--
1312 g.writeln(');')
1313 g.indent--
1314 g.writeln('}')
1315}
1316
1317fn (mut g Gen) write_orm_field_access_to_primitive(field ast.StructField, object_var string, table_typ ast.Type,
1318 inserting_object_sym &ast.TypeSymbol) {
1319 final_field_typ := g.table.final_type(field.typ)
1320 mut sym := g.table.sym(final_field_typ)
1321 mut typ := g.orm_primitive_field_name(field.typ)
1322 mut ctyp := sym.cname
1323 if sym.kind == .struct && ctyp != 'time__Time' {
1324 foreign_info := sym.info as ast.Struct
1325 primary_field := g.get_orm_struct_primary_field(foreign_info.fields) or {
1326 verror('ORM: struct field `${field.name}` of type `${sym.name}` has no primary field')
1327 }
1328 g.write_orm_field_access_to_primitive(primary_field,
1329 '${object_var}.${orm_field_access_name(field.name)}',
1330 final_field_typ.clear_flag(.option), g.table.sym(final_field_typ.clear_flag(.option)))
1331 return
1332 }
1333 typ = vint2int(typ)
1334 field_access := if inserting_object_sym.kind == .sum_type {
1335 table_sym := g.table.sym(table_typ)
1336 '(*${object_var}._${table_sym.cname}).${orm_field_access_name(field.name)}'
1337 } else {
1338 '${object_var}.${orm_field_access_name(field.name)}'
1339 }
1340 if final_field_typ.has_flag(.option) {
1341 g.writeln('${field_access}.state == 2 ? _const_orm__null_primitive : orm__${typ}_to_primitive(*(${ctyp}*)(${field_access}.data)),')
1342 } else {
1343 g.writeln('orm__${typ}_to_primitive(${field_access}),')
1344 }
1345}
1346
1347fn get_orm_field_by_struct_field_name(fields []ast.StructField, name string) ?ast.StructField {
1348 for field in fields {
1349 if field.name == name {
1350 return field
1351 }
1352 }
1353 return none
1354}
1355
1356// write_orm_delete writes C code that calls ORM functions for deleting rows.
1357fn (mut g Gen) write_orm_delete(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, _ []ast.Attr) {
1358 g.writeln('// sql { delete from `${table_name}` }')
1359 g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method__v_delete(')
1360 g.indent++
1361 g.writeln('${connection_var_name}._object, // Connection object')
1362 g.write_orm_table_struct(node.table_expr.typ)
1363 g.writeln(',')
1364 g.write_orm_where(node.where_expr)
1365 g.indent--
1366 g.writeln(');')
1367}
1368
1369// write_orm_insert_with_last_ids writes C code that calls ORM functions for
1370// inserting a struct into a table, saving inserted `id` into a passed variable.
1371fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_var_name string, table_name string,
1372 last_ids_arr string, res string, pid string, fkey string, or_expr ast.OrExpr) {
1373 mut subs := []ast.SqlStmtLine{}
1374
1375 mut subs_unwrapped_c_typ := []string{}
1376 mut arrs := []ast.SqlStmtLine{}
1377 mut fkeys := []string{}
1378 mut field_names := []string{}
1379 mut opt_fields := []int{}
1380
1381 for field in node.fields {
1382 final_field_typ := g.table.final_type(field.typ)
1383 sym := g.table.sym(final_field_typ)
1384 if sym.kind == .struct && sym.name != 'time.Time' {
1385 if field.name in node.sub_structs {
1386 subs << unsafe { node.sub_structs[field.name] }
1387
1388 unwrapped_c_typ := g.styp(final_field_typ.clear_flag(.option))
1389 subs_unwrapped_c_typ << if final_field_typ.has_flag(.option) {
1390 unwrapped_c_typ
1391 } else {
1392 ''
1393 }
1394 }
1395 } else if sym.kind == .array {
1396 // Handle foreign keys
1397 if attr := field.attrs.find_first('fkey') {
1398 fkeys << attr.arg
1399 } else {
1400 verror('missing fkey attribute')
1401 }
1402 if final_field_typ.has_flag(.option) {
1403 opt_fields << arrs.len
1404 }
1405 if field.name in node.sub_structs {
1406 arrs << unsafe { node.sub_structs[field.name] }
1407 }
1408 field_names << field.name
1409 }
1410 }
1411
1412 fields := g.orm_non_array_fields(node.fields)
1413 auto_fields := get_auto_field_idxs(fields)
1414
1415 primary_field := g.get_orm_struct_primary_field(fields) or { ast.StructField{} }
1416
1417 is_serial := (primary_field.attrs.contains_arg('sql', 'serial')
1418 || primary_field.attrs.contains('serial')) && primary_field.typ == ast.int_type
1419
1420 mut inserting_object_type := ast.void_type
1421 mut member_access_type := '.'
1422 if node.scope != unsafe { nil } {
1423 if inserting_object := node.scope.find(node.object_var) {
1424 if inserting_object.typ.is_ptr() {
1425 member_access_type = '->'
1426 }
1427 inserting_object_type = g.orm_inserting_object_type(inserting_object)
1428 } else {
1429 inserting_object_type = node.table_expr.typ
1430 }
1431 }
1432
1433 inserting_object_sym := g.table.sym(inserting_object_type)
1434 for i, mut sub in subs {
1435 if subs_unwrapped_c_typ[i].len > 0 {
1436 var := '${node.object_var}${member_access_type}${sub.object_var}'
1437 g.writeln('if(${var}.state == 0) {')
1438 g.indent++
1439 sub.object_var = '(*(${subs_unwrapped_c_typ[i]}*)${node.object_var}${member_access_type}${sub.object_var}.data)'
1440 } else {
1441 sub.object_var = '${node.object_var}${member_access_type}${sub.object_var}'
1442 }
1443 g.sql_stmt_line(sub, connection_var_name, or_expr)
1444 g.writeln('builtin__array_push(&${last_ids_arr}, _MOV((orm__Primitive[1]){')
1445 g.writeln('\torm__int_to_primitive(orm__Connection_name_table[${connection_var_name}._typ]._method_last_id(${connection_var_name}._object))}));')
1446 if subs_unwrapped_c_typ[i].len > 0 {
1447 g.indent--
1448 g.writeln('} else {')
1449 g.writeln('\tbuiltin__array_push(&${last_ids_arr}, _MOV((orm__Primitive[1]){ _const_orm__null_primitive }));')
1450 g.writeln('}')
1451 }
1452 }
1453
1454 g.writeln('// sql { insert into `${table_name}` }')
1455 g.writeln('${result_name}_void ${res} = orm__Connection_name_table[${connection_var_name}._typ]._method_insert(')
1456 g.indent++
1457 g.writeln('${connection_var_name}._object, // Connection object')
1458 g.write_orm_table_struct(node.table_expr.typ)
1459 g.writeln(',')
1460 g.writeln('(orm__QueryData){')
1461 g.indent++
1462 g.writeln('.fields = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),')
1463 g.indent++
1464
1465 if fields.len > 0 {
1466 g.writeln('_MOV((string[${fields.len}]){ ')
1467 g.indent++
1468 for f in fields {
1469 g.writeln('_S("${g.get_orm_column_name_from_struct_field(f)}"),')
1470 }
1471 g.indent--
1472 g.writeln('})')
1473 } else {
1474 g.writeln('NULL')
1475 }
1476 g.indent--
1477 g.writeln('),')
1478
1479 g.writeln('.data = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(orm__Primitive),')
1480 g.indent++
1481
1482 if fields.len > 0 {
1483 g.writeln('_MOV((orm__Primitive[${fields.len}]){')
1484 g.indent++
1485 mut structs := 0
1486 for field in fields {
1487 if field.name == fkey {
1488 g.writeln('${pid}, ')
1489 continue
1490 }
1491 final_field_typ := g.table.final_type(field.typ)
1492 mut sym := g.table.sym(final_field_typ)
1493 mut typ := g.orm_primitive_field_name(field.typ)
1494 mut ctyp := sym.cname
1495 if sym.kind == .struct && ctyp != 'time__Time' {
1496 g.writeln('(*(orm__Primitive*) builtin__array_get(${last_ids_arr}, ${structs})),')
1497 structs++
1498 continue
1499 }
1500 // fields processed hereafter can be NULL...
1501 typ = vint2int(typ)
1502 var := '${node.object_var}${member_access_type}${orm_field_access_name(field.name)}'
1503 if final_field_typ.has_flag(.option) {
1504 g.writeln('${var}.state == 2? _const_orm__null_primitive : orm__${typ}_to_primitive(*(${ctyp}*)(${var}.data)),')
1505 } else if inserting_object_sym.kind == .sum_type {
1506 table_sym := g.table.sym(node.table_expr.typ)
1507 sum_type_var := '(*${node.object_var}._${table_sym.cname})${member_access_type}${orm_field_access_name(field.name)}'
1508 g.writeln('orm__${typ}_to_primitive(${sum_type_var}),')
1509 } else {
1510 g.writeln('orm__${typ}_to_primitive(${var}),')
1511 }
1512 }
1513 g.indent--
1514 g.writeln('})')
1515 } else {
1516 g.write('NULL')
1517 }
1518 g.indent--
1519 g.writeln('),')
1520 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1521 if auto_fields.len > 0 {
1522 g.writeln('.auto_fields = builtin__new_array_from_c_array(${auto_fields.len}, ${auto_fields.len}, sizeof(${ast.int_type_name}),')
1523 g.indent++
1524 g.write('_MOV((${ast.int_type_name}[${auto_fields.len}]){')
1525 for i in auto_fields {
1526 g.write(' ${i},')
1527 }
1528 g.writeln(' })),')
1529 g.indent--
1530 } else {
1531 g.writeln('.auto_fields = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1532 }
1533 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
1534 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
1535 g.indent--
1536 g.writeln('}')
1537 g.indent--
1538 g.writeln(');')
1539 // Validate main insertion success otherwise, handled and propagated error.
1540 g.or_block(res, or_expr, ast.int_type.set_flag(.result))
1541
1542 if arrs.len > 0 {
1543 mut id_name := g.new_tmp_var()
1544 if is_serial {
1545 // use last_insert_id if current struct has `int [primary; sql: serial]`
1546 g.writeln('orm__Primitive ${id_name} = orm__int_to_primitive(orm__Connection_name_table[${connection_var_name}._typ]._method_last_id(${connection_var_name}._object));')
1547 } else {
1548 // else use the primary key value
1549 mut typ := g.orm_primitive_field_name(primary_field.typ)
1550 typ = vint2int(typ)
1551 g.writeln('orm__Primitive ${id_name} = orm__${typ}_to_primitive(${node.object_var}${member_access_type}${orm_field_access_name(primary_field.name)});')
1552 }
1553 for i, mut arr in arrs {
1554 idx := g.new_tmp_var()
1555 ctyp := g.styp(arr.table_expr.typ)
1556 is_option := opt_fields.contains(i)
1557 if is_option {
1558 g.writeln('for (${ast.int_type_name} ${idx} = 0; ${node.object_var}${member_access_type}${arr.object_var}.state != 2 && ${idx} < (*(Array_${ctyp}*)${node.object_var}${member_access_type}${arr.object_var}.data).len; ${idx}++) {')
1559 } else {
1560 g.writeln('for (${ast.int_type_name} ${idx} = 0; ${idx} < ${node.object_var}${member_access_type}${arr.object_var}.len; ${idx}++) {')
1561 }
1562 g.indent++
1563 last_ids := g.new_tmp_var()
1564 res_ := g.new_tmp_var()
1565 tmp_var := g.new_tmp_var()
1566 g.writeln('Array_orm__Primitive ${last_ids} = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
1567 if is_option {
1568 g.writeln('${ctyp} ${tmp_var} = (*(${ctyp}*)builtin__array_get(*(Array_${ctyp}*)${node.object_var}${member_access_type}${arr.object_var}.data, ${idx}));')
1569 } else {
1570 g.writeln('${ctyp} ${tmp_var} = (*(${ctyp}*)builtin__array_get(${node.object_var}${member_access_type}${arr.object_var}, ${idx}));')
1571 }
1572 arr.object_var = tmp_var
1573 mut fff := []ast.StructField{}
1574 for f in arr.fields {
1575 mut skip := false
1576 for attr in f.attrs {
1577 if attr.name == 'skip' {
1578 skip = true
1579 }
1580 if attr.name == 'sql' && attr.arg == '-' {
1581 skip = true
1582 }
1583 }
1584 if !skip {
1585 fff << f
1586 }
1587 }
1588 arr.fields = fff.clone()
1589 unsafe { fff.free() }
1590 g.write_orm_insert_with_last_ids(arr, connection_var_name,
1591 g.get_table_name_by_struct_type(arr.table_expr.typ), last_ids, res_, id_name,
1592 fkeys[i], or_expr)
1593 g.indent--
1594 g.writeln('}')
1595 }
1596 }
1597}
1598
1599// write_orm_expr_to_primitive writes C code for casting expressions into a primitive type
1600// by checking support expressions and their types.
1601fn (mut g Gen) write_orm_expr_to_primitive(expr ast.Expr) {
1602 match expr {
1603 ast.InfixExpr {
1604 g.write_orm_primitive(g.table.find_type('orm.InfixType'), expr)
1605 }
1606 ast.StringLiteral {
1607 g.write_orm_primitive(ast.string_type, expr)
1608 }
1609 ast.StringInterLiteral {
1610 g.write_orm_primitive(ast.string_type, expr)
1611 }
1612 ast.IntegerLiteral {
1613 g.write_orm_primitive(ast.int_type, expr)
1614 }
1615 ast.BoolLiteral {
1616 g.write_orm_primitive(ast.bool_type, expr)
1617 }
1618 ast.EnumVal {
1619 g.write_orm_primitive(ast.i64_type, expr)
1620 }
1621 ast.Ident {
1622 info := expr.info as ast.IdentVar
1623 typ := g.orm_if_guard_ident_type(expr, info.typ)
1624 g.write_orm_primitive(orm_expr_effective_type(expr, typ), expr)
1625 }
1626 ast.SelectorExpr {
1627 g.write_orm_primitive(orm_expr_effective_type(expr, expr.typ), expr)
1628 }
1629 ast.CallExpr {
1630 g.write_orm_primitive(orm_expr_effective_type(expr, expr.return_type), expr)
1631 }
1632 ast.CastExpr {
1633 g.write_orm_primitive(expr.typ, expr)
1634 }
1635 ast.Nil {
1636 g.write_orm_primitive(ast.none_type, expr)
1637 }
1638 ast.IfExpr {
1639 g.write_orm_primitive(expr.typ, expr)
1640 }
1641 ast.None {
1642 g.write_orm_primitive(ast.none_type, expr)
1643 }
1644 else {
1645 verror('ORM: ${expr.type_name()} is not supported')
1646 }
1647 }
1648}
1649
1650fn (mut g Gen) write_orm_struct_field_expr_to_primitive(field ast.StructField, expr ast.Expr) {
1651 if expr is ast.None || expr is ast.Nil {
1652 g.writeln('_const_orm__null_primitive,')
1653 return
1654 }
1655 final_field_typ := g.table.final_type(field.typ.clear_flag(.option))
1656 foreign_sym := g.table.sym(final_field_typ)
1657 foreign_info := foreign_sym.info as ast.Struct
1658 primary_field := g.get_orm_struct_primary_field(foreign_info.fields) or {
1659 verror('ORM: struct field `${field.name}` of type `${foreign_sym.name}` has no primary field')
1660 }
1661 g.write_orm_primitive(primary_field.typ, ast.SelectorExpr{
1662 pos: expr.pos()
1663 field_name: primary_field.name
1664 expr: expr
1665 expr_type: orm_expr_effective_type(expr, field.typ).clear_flag(.option).clear_flag(.result)
1666 typ: primary_field.typ
1667 scope: unsafe { nil }
1668 })
1669}
1670
1671fn orm_expr_effective_type(expr ast.Expr, typ ast.Type) ast.Type {
1672 return match expr {
1673 ast.CallExpr {
1674 if expr.or_block.kind != .absent {
1675 typ.clear_option_and_result()
1676 } else {
1677 typ
1678 }
1679 }
1680 ast.Ident {
1681 if expr.or_expr.kind != .absent {
1682 typ.clear_option_and_result()
1683 } else {
1684 typ
1685 }
1686 }
1687 ast.SelectorExpr {
1688 if expr.or_block.kind != .absent {
1689 typ.clear_option_and_result()
1690 } else {
1691 typ
1692 }
1693 }
1694 else {
1695 typ
1696 }
1697 }
1698}
1699
1700// write_orm_primitive writes C code for casting expressions into a primitive type,
1701// which will be used in low-level database libs.
1702fn (mut g Gen) write_orm_primitive(t ast.Type, expr ast.Expr) {
1703 if t == 0 {
1704 verror('${g.file.path}:${expr.pos().line_nr + 1}: ORM: unknown type t == 0\nexpr: ${expr}\nlast SQL stmt:\n${g.out.after(g.sql_last_stmt_out_len)}')
1705 }
1706 final_field_typ := g.table.final_type(t)
1707 mut sym := g.table.sym(final_field_typ)
1708 mut typ := sym.cname
1709 if typ == 'orm__Primitive' {
1710 g.expr(expr)
1711 g.writeln(',')
1712 return
1713 }
1714 if typ == 'none' {
1715 g.writeln('_const_orm__null_primitive,')
1716 return
1717 }
1718
1719 if expr is ast.InfixExpr {
1720 g.writeln('orm__infix_to_primitive((orm__InfixType){')
1721 g.indent++
1722 g.write('.name = _S("${expr.left}"),')
1723 mut kind := match expr.op {
1724 .plus {
1725 'orm__MathOperationKind__add'
1726 }
1727 .minus {
1728 'orm__MathOperationKind__sub'
1729 }
1730 .div {
1731 'orm__MathOperationKind__div'
1732 }
1733 .mul {
1734 'orm__MathOperationKind__mul'
1735 }
1736 else {
1737 ''
1738 }
1739 }
1740
1741 g.writeln(' .operator = ${kind},')
1742 g.write(' .right = ')
1743 g.write_orm_expr_to_primitive(expr.right)
1744 g.indent--
1745 g.writeln(' }),')
1746 } else {
1747 if typ == 'time__Time' {
1748 typ = 'time'
1749 }
1750
1751 if t.has_flag(.option) {
1752 typ = 'option_${typ}'
1753 } else if g.table.final_sym(t.clear_flag(.option)).kind == .enum {
1754 typ = 'i64'
1755 } else if g.table.final_sym(t).kind == .array {
1756 typ = g.table.sym(g.table.final_type(t)).cname.to_lower()
1757 }
1758 typ = vint2int(typ)
1759 g.write('orm__${typ}_to_primitive(')
1760 if expr is ast.CallExpr {
1761 g.call_expr(expr)
1762 } else {
1763 g.left_is_opt = true
1764 g.expr(expr)
1765 }
1766 g.writeln('),')
1767 }
1768}
1769
1770// write_orm_where writes C code that generates
1771// the `QueryData` structure for passing it into ORM methods.
1772fn (mut g Gen) write_orm_where(where_expr ast.Expr) {
1773 mut fields := []string{}
1774 mut kinds := []string{}
1775 mut parentheses := [][]int{}
1776 mut data := []ast.Expr{}
1777 mut is_ands := []bool{}
1778
1779 g.writeln('// ORM where')
1780
1781 g.writeln('(orm__QueryData){')
1782 g.indent++
1783 g.write_orm_where_expr(where_expr, mut fields, mut parentheses, mut kinds, mut data, mut
1784 is_ands)
1785 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1786 if fields.len > 0 {
1787 g.writeln('.fields = builtin__new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),')
1788 g.indent++
1789 g.writeln('_MOV((string[${fields.len}]){')
1790 g.indent++
1791 for field in fields {
1792 g.writeln('_S("${field}"),')
1793 }
1794 g.indent--
1795 g.writeln('})')
1796 g.indent--
1797 } else {
1798 g.writeln('.fields = builtin____new_array_with_default_noscan(${fields.len}, ${fields.len}, sizeof(string), 0')
1799 }
1800 g.writeln('),')
1801
1802 g.writeln('.data = builtin__new_array_from_c_array(${data.len}, ${data.len}, sizeof(orm__Primitive),')
1803 g.indent++
1804 if data.len > 0 {
1805 g.writeln('_MOV((orm__Primitive[${data.len}]){')
1806 g.indent++
1807 for e in data {
1808 g.write_orm_expr_to_primitive(e)
1809 }
1810 g.indent--
1811 g.writeln('})')
1812 } else {
1813 g.writeln('0')
1814 }
1815 g.indent--
1816 g.writeln('),')
1817
1818 g.write('.parentheses = ')
1819 if parentheses.len > 0 {
1820 g.write('builtin__new_array_from_c_array(${parentheses.len}, ${parentheses.len}, sizeof(Array_${ast.int_type_name}), _MOV((Array_${ast.int_type_name}[${parentheses.len}]){')
1821 for par in parentheses {
1822 if par.len > 0 {
1823 g.write('builtin__new_array_from_c_array(${par.len}, ${par.len}, sizeof(${ast.int_type_name}), _MOV((${ast.int_type_name}[${par.len}]){')
1824 for val in par {
1825 g.write('${val},')
1826 }
1827 g.write('})),')
1828 } else {
1829 g.write('builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
1830 }
1831 }
1832 g.write('}))')
1833 } else {
1834 g.write('builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0)')
1835 }
1836 g.writeln(',')
1837
1838 if kinds.len > 0 {
1839 g.writeln('.kinds = builtin__new_array_from_c_array(${kinds.len}, ${kinds.len}, sizeof(orm__OperationKind),')
1840 g.indent++
1841 g.writeln('_MOV((orm__OperationKind[${kinds.len}]){')
1842 g.indent++
1843 for k in kinds {
1844 g.writeln('${k},')
1845 }
1846 g.indent--
1847 g.writeln('})')
1848 g.indent--
1849 } else {
1850 g.write('.kinds = builtin____new_array_with_default_noscan(${kinds.len}, ${kinds.len}, sizeof(orm__OperationKind), 0')
1851 }
1852 g.writeln('),')
1853
1854 if is_ands.len > 0 {
1855 g.write('.is_and = builtin__new_array_from_c_array(${is_ands.len}, ${is_ands.len}, sizeof(bool),')
1856 g.indent++
1857 g.writeln('_MOV((bool[${is_ands.len}]){')
1858 g.indent++
1859 for is_and in is_ands {
1860 g.writeln('${is_and},')
1861 }
1862 g.indent--
1863 g.write('})')
1864 g.indent--
1865 } else {
1866 g.write('.is_and = builtin____new_array_with_default_noscan(${is_ands.len}, ${is_ands.len}, sizeof(bool), 0')
1867 }
1868 g.indent--
1869 g.writeln2('),', '}')
1870}
1871
1872// write_orm_where_expr writes C code that generates expression which is used in the `QueryData`.
1873fn (mut g Gen) write_orm_where_expr(expr ast.Expr, mut fields []string, mut parentheses [][]int, mut kinds []string,
1874 mut data []ast.Expr, mut is_and []bool) {
1875 match expr {
1876 ast.InfixExpr {
1877 g.sql_side = .left
1878 g.write_orm_where_expr(expr.left, mut fields, mut parentheses, mut kinds, mut data, mut
1879 is_and)
1880 is_nil_comparison := expr.right is ast.Nil && expr.op in [.eq, .ne]
1881 mut ignore_rhs := expr.op in [.key_is, .not_is] || is_nil_comparison
1882 mut kind := match expr.op {
1883 .ne {
1884 if is_nil_comparison {
1885 'orm__OperationKind__is_not_null'
1886 } else {
1887 'orm__OperationKind__neq'
1888 }
1889 }
1890 .eq {
1891 if is_nil_comparison {
1892 'orm__OperationKind__is_null'
1893 } else {
1894 'orm__OperationKind__eq'
1895 }
1896 }
1897 .lt {
1898 'orm__OperationKind__lt'
1899 }
1900 .gt {
1901 'orm__OperationKind__gt'
1902 }
1903 .ge {
1904 'orm__OperationKind__ge'
1905 }
1906 .le {
1907 'orm__OperationKind__le'
1908 }
1909 .key_like {
1910 'orm__OperationKind__orm_like'
1911 }
1912 .key_ilike {
1913 'orm__OperationKind__orm_ilike'
1914 }
1915 .key_is {
1916 'orm__OperationKind__is_null'
1917 }
1918 .not_is {
1919 'orm__OperationKind__is_not_null'
1920 }
1921 .key_in {
1922 'orm__OperationKind__in'
1923 }
1924 .not_in {
1925 'orm__OperationKind__not_in'
1926 }
1927 else {
1928 ''
1929 }
1930 }
1931
1932 if kind == '' {
1933 if expr.op == .logical_or {
1934 is_and << false
1935 } else if expr.op == .and {
1936 is_and << true
1937 } else {
1938 kind = 'orm__OperationKind__eq'
1939 }
1940 }
1941 if expr.left !is ast.InfixExpr && expr.right !is ast.InfixExpr && kind != '' {
1942 kinds << kind
1943 }
1944 if !ignore_rhs { // ignore rhs for unary ops and SQL NULL equality
1945 g.sql_side = .right
1946 g.write_orm_where_expr(expr.right, mut fields, mut parentheses, mut kinds, mut
1947 data, mut is_and)
1948 }
1949 }
1950 ast.ParExpr {
1951 mut par := [fields.len]
1952 g.write_orm_where_expr(expr.expr, mut fields, mut parentheses, mut kinds, mut data, mut
1953 is_and)
1954 par << fields.len - 1
1955 parentheses << par
1956 }
1957 ast.Ident {
1958 if g.sql_side == .left {
1959 field := g.get_orm_current_table_field(expr.name) or {
1960 verror('field "${expr.name}" does not exist on "${g.sql_table_name}"')
1961 }
1962 fields << g.get_orm_column_name_from_struct_field(field)
1963 } else {
1964 data << expr
1965 }
1966 }
1967 ast.StringLiteral {
1968 data << expr
1969 }
1970 ast.StringInterLiteral {
1971 data << expr
1972 }
1973 ast.IntegerLiteral {
1974 data << expr
1975 }
1976 ast.SelectorExpr {
1977 if g.comptime.is_comptime_selector_field_name(expr, 'name') {
1978 fields << g.comptime.comptime_for_field_value.name
1979 } else {
1980 data << expr
1981 }
1982 }
1983 ast.BoolLiteral {
1984 data << expr
1985 }
1986 ast.EnumVal {
1987 data << expr
1988 }
1989 ast.CallExpr {
1990 data << expr
1991 }
1992 ast.None {
1993 data << expr
1994 }
1995 else {}
1996 }
1997}
1998
1999// write_orm_select writes C code that calls ORM functions for selecting rows,
2000// storing the result in the provided variable name
2001fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, result_var string) {
2002 mut fields := []ast.StructField{}
2003 mut primary_field := g.get_orm_struct_primary_field(node.fields) or { ast.StructField{} }
2004 mut dynamic_where_var := ''
2005
2006 for field in node.fields {
2007 mut skip := false
2008 for attr in field.attrs {
2009 if attr.name == 'skip' {
2010 skip = true
2011 }
2012 if attr.name == 'sql' && attr.arg == '-' {
2013 skip = true
2014 }
2015 }
2016 if !skip {
2017 fields << field
2018 }
2019 }
2020
2021 select_result_var_name := g.new_tmp_var()
2022 table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
2023 g.sql_table_name = g.table.sym(node.table_expr.typ).name
2024 g.sql_table_typ = node.table_expr.typ
2025 if node.has_where && node.is_dynamic {
2026 dynamic_where_var = g.emit_dynamic_sql_query_data(node.where_expr)
2027 }
2028
2029 g.writeln('// sql { select from `${table_name}` }')
2030 g.writeln('${result_name}_Array_Array_orm__Primitive ${select_result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_select(')
2031 g.indent++
2032 g.writeln('${connection_var_name}._object, // Connection object')
2033 g.writeln('(orm__SelectConfig){')
2034 g.indent++
2035 g.writeln('.table = ')
2036 g.write_orm_table_struct(node.table_expr.typ)
2037 g.writeln(',')
2038 g.writeln('.aggregate_kind = orm__AggregateKind__${node.aggregate_kind},')
2039 g.writeln('.aggregate_field = _S("${node.aggregate_field}"),')
2040 g.writeln('.has_where = ${node.has_where},')
2041 g.writeln('.has_order = ${node.has_order},')
2042
2043 if node.has_order {
2044 g.write('.order = _S("')
2045 if node.order_expr is ast.Ident {
2046 field := g.get_orm_current_table_field(node.order_expr.name) or {
2047 verror('field "${node.order_expr.name}" does not exist on "${g.sql_table_name}"')
2048 }
2049 g.write(g.get_orm_column_name_from_struct_field(field))
2050 } else {
2051 g.expr(node.order_expr)
2052 }
2053 g.writeln('"),')
2054 if node.has_desc {
2055 g.writeln('.order_type = orm__OrderType__desc,')
2056 } else {
2057 g.writeln('.order_type = orm__OrderType__asc,')
2058 }
2059 }
2060
2061 g.writeln('.has_limit = ${node.has_limit},')
2062 g.writeln('.has_offset = ${node.has_offset},')
2063 g.writeln('.has_distinct = ${node.has_distinct},')
2064
2065 if primary_field.name != '' {
2066 g.writeln('.primary = _S("${primary_field.name}"),')
2067 }
2068
2069 mut select_fields := g.orm_non_array_fields(fields)
2070 if node.aggregate_kind != .none {
2071 select_fields = fields.clone()
2072 }
2073 g.writeln('.fields = builtin__new_array_from_c_array(${select_fields.len}, ${select_fields.len}, sizeof(string),')
2074 g.indent++
2075 mut types := []string{}
2076 mut select_exprs := []string{}
2077 if select_fields.len > 0 {
2078 g.writeln('_MOV((string[${select_fields.len}]){')
2079 g.indent++
2080 for field in select_fields {
2081 column_name := g.get_orm_column_name_from_struct_field(field)
2082 g.writeln('_S("${column_name}"),')
2083 select_exprs << g.get_orm_select_expr_from_struct_field(field)
2084 mut final_field_typ := g.table.final_type(field.typ.clear_flag(.option))
2085 if node.aggregate_kind == .avg {
2086 final_field_typ = ast.f64_type
2087 } else if node.aggregate_kind == .count {
2088 final_field_typ = ast.int_type
2089 }
2090 sym := g.table.sym(final_field_typ)
2091 if sym.name == 'time.Time' {
2092 types << '_const_orm__time_'
2093 continue
2094 }
2095 if sym.kind == .struct {
2096 types << int(ast.int_type).str()
2097 continue
2098 } else if sym.kind == .enum {
2099 types << '_const_orm__enum_'
2100 continue
2101 }
2102 types << final_field_typ.idx().str()
2103 }
2104 g.indent--
2105 g.writeln('})')
2106 } else {
2107 g.writeln('NULL')
2108 }
2109 g.indent--
2110 g.writeln2('),',
2111 '.select_exprs = builtin__new_array_from_c_array(${select_exprs.len}, ${select_exprs.len}, sizeof(string),')
2112 g.indent++
2113
2114 if select_exprs.len > 0 {
2115 g.writeln('_MOV((string[${select_exprs.len}]){')
2116 g.indent++
2117 for select_expr in select_exprs {
2118 expr1 := util.smart_quote(select_expr, false)
2119 expr := cescape_nonascii(expr1)
2120 g.writeln('_S("${expr}"),')
2121 }
2122 g.indent--
2123 g.writeln('})')
2124 } else {
2125 g.writeln('NULL')
2126 }
2127 g.indent--
2128 g.writeln2('),',
2129 '.types = builtin__new_array_from_c_array(${types.len}, ${types.len}, sizeof(${ast.int_type_name}),')
2130 g.indent++
2131
2132 if types.len > 0 {
2133 g.write('_MOV((${ast.int_type_name}[${types.len}]){')
2134 for typ in types {
2135 g.write(' ${typ},')
2136 }
2137 g.writeln(' })')
2138 } else {
2139 g.writeln('NULL')
2140 }
2141 g.indent--
2142 g.writeln('),')
2143 // Generate JOIN clauses array
2144 g.write_orm_joins(node.joins)
2145 g.indent--
2146 g.writeln('},')
2147
2148 mut exprs := []ast.Expr{}
2149 if node.has_limit {
2150 exprs << node.limit_expr
2151 }
2152 if node.has_offset {
2153 exprs << node.offset_expr
2154 }
2155
2156 g.writeln('(orm__QueryData) {')
2157 g.indent++
2158 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
2159 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
2160 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
2161 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
2162 if exprs.len > 0 {
2163 g.write('.data = builtin__new_array_from_c_array(${exprs.len}, ${exprs.len}, sizeof(orm__Primitive),')
2164 g.write(' _MOV((orm__Primitive[${exprs.len}]){')
2165 for e in exprs {
2166 g.write_orm_expr_to_primitive(e)
2167 }
2168 g.writeln('})')
2169 } else {
2170 g.writeln('.data = builtin____new_array_with_default_noscan(${exprs.len}, ${exprs.len}, sizeof(orm__Primitive), 0')
2171 }
2172 g.indent--
2173 g.writeln(')},')
2174
2175 if node.has_where {
2176 if node.is_dynamic {
2177 g.writeln('${dynamic_where_var}')
2178 } else {
2179 g.write_orm_where(node.where_expr)
2180 }
2181 } else {
2182 g.writeln('(orm__QueryData) {')
2183 g.indent++
2184 g.writeln('.types = builtin____new_array_with_default_noscan(0, 0, sizeof(${ast.int_type_name}), 0),')
2185 g.writeln('.kinds = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
2186 g.writeln('.is_and = builtin____new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
2187 g.writeln('.parentheses = builtin____new_array_with_default_noscan(0, 0, sizeof(Array_${ast.int_type_name}), 0),')
2188 g.writeln('.data = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0)')
2189 g.indent--
2190 g.writeln('}')
2191 }
2192 g.indent--
2193 g.writeln(');')
2194
2195 g.writeln('${result_var}.is_error = ${select_result_var_name}.is_error;')
2196 g.writeln('${result_var}.err = ${select_result_var_name}.err;')
2197 g.or_block(result_var, node.or_expr, node.typ)
2198
2199 // or_block could have ended in return (longjump) or could have
2200 // yielded another value, so we must test for on non-error result
2201 g.writeln('if (!${result_var}.is_error) {')
2202 g.indent++
2203
2204 unwrapped_c_typ := g.styp(node.typ.clear_flag(.result))
2205 select_unwrapped_result_var_name := g.new_tmp_var()
2206
2207 g.writeln('Array_Array_orm__Primitive ${select_unwrapped_result_var_name} = (*(Array_Array_orm__Primitive*)${select_result_var_name}.data);')
2208
2209 if node.aggregate_kind != .none {
2210 prim_var := g.new_tmp_var()
2211 aggregate_type := if node.aggregate_kind == .avg {
2212 ast.f64_type
2213 } else {
2214 node.aggregate_field_type
2215 }
2216 primitive_field_name := g.orm_primitive_variant_field_name(aggregate_type)
2217 aggregate_value_styp := g.styp(aggregate_type.clear_flag(.option))
2218 if node.aggregate_kind == .count {
2219 g.writeln('*(${unwrapped_c_typ}*) ${result_var}.data = 0;')
2220 g.writeln('if (${select_unwrapped_result_var_name}.len > 0 && (*(Array_orm__Primitive*) builtin__array_get(${select_unwrapped_result_var_name}, 0)).len > 0) {')
2221 g.indent++
2222 g.writeln('orm__Primitive *${prim_var} = &(*(orm__Primitive*) builtin__array_get((*(Array_orm__Primitive*) builtin__array_get(${select_unwrapped_result_var_name}, 0)), 0));')
2223 g.writeln('*(${unwrapped_c_typ}*) ${result_var}.data = *(${prim_var}->_${primitive_field_name});')
2224 g.indent--
2225 g.writeln('}')
2226 } else {
2227 aggregate_result_var := g.new_tmp_var()
2228 g.writeln('${unwrapped_c_typ} ${aggregate_result_var} = (${unwrapped_c_typ}){ .state = 2, .err = _const_none__, .data = {E_STRUCT} };')
2229 g.writeln('if (${select_unwrapped_result_var_name}.len > 0 && (*(Array_orm__Primitive*) builtin__array_get(${select_unwrapped_result_var_name}, 0)).len > 0) {')
2230 g.indent++
2231 g.writeln('orm__Primitive *${prim_var} = &(*(orm__Primitive*) builtin__array_get((*(Array_orm__Primitive*) builtin__array_get(${select_unwrapped_result_var_name}, 0)), 0));')
2232 g.writeln('if (${prim_var}->_typ != ${g.table.find_type_idx('orm.Null')}) {')
2233 g.indent++
2234 g.writeln('builtin___option_ok(${prim_var}->_${primitive_field_name}, (_option *)&${aggregate_result_var}, sizeof(${aggregate_value_styp}));')
2235 g.indent--
2236 g.writeln('}')
2237 g.indent--
2238 g.writeln('}')
2239 g.writeln('*(${unwrapped_c_typ}*) ${result_var}.data = ${aggregate_result_var};')
2240 }
2241 } else {
2242 tmp := g.new_tmp_var()
2243 idx := g.new_tmp_var()
2244 g.writeln('${ast.int_type_name} ${idx} = 0;')
2245 mut typ_str := ''
2246 if node.is_array {
2247 info := g.table.sym(node.typ).array_info()
2248 typ_str = g.styp(info.elem_type)
2249 base_typ := g.base_type(node.typ)
2250 if node.typ.has_flag(.option) {
2251 g.writeln('${unwrapped_c_typ} ${tmp}_array = { .state = 2, .err = _const_none__, .data = {E_STRUCT} };')
2252 g.writeln('builtin___option_ok(&(${base_typ}[]) { builtin____new_array(0, ${select_unwrapped_result_var_name}.len, sizeof(${typ_str})) }, (_option *)&${tmp}_array, sizeof(${base_typ}));')
2253 } else {
2254 g.writeln('${unwrapped_c_typ} ${tmp}_array = builtin____new_array(0, ${select_unwrapped_result_var_name}.len, sizeof(${typ_str}));')
2255 }
2256 g.writeln('for (; ${idx} < ${select_unwrapped_result_var_name}.len; ${idx}++) {')
2257 g.indent++
2258 g.write('${typ_str} ${tmp} = (${typ_str}) {')
2259 inf := g.table.sym(info.elem_type).struct_info()
2260 for i, field in inf.fields {
2261 g.zero_struct_field(field)
2262 if i != inf.fields.len - 1 {
2263 g.write(', ')
2264 }
2265 }
2266 g.writeln('};')
2267 } else {
2268 g.write('${unwrapped_c_typ} ${tmp} = (${unwrapped_c_typ}){')
2269 info := g.table.sym(node.typ).struct_info()
2270 for i, field in info.fields {
2271 g.zero_struct_field(field)
2272 if i != info.fields.len - 1 {
2273 g.write(', ')
2274 }
2275 }
2276 g.writeln('};')
2277 }
2278
2279 g.writeln('if (${select_unwrapped_result_var_name}.len > 0) {')
2280 g.indent++
2281
2282 mut fields_idx := 0
2283 for field in fields {
2284 array_get_call_code := '(*(orm__Primitive*) builtin__array_get((*(Array_orm__Primitive*) builtin__array_get(${select_unwrapped_result_var_name}, ${idx})), ${fields_idx}))'
2285 final_field_typ := g.table.final_type(field.typ)
2286 sym := g.table.sym(final_field_typ)
2287 field_var := '${tmp}.${orm_field_access_name(field.name)}'
2288 field_c_typ := g.styp(final_field_typ)
2289 if sym.kind == .struct && sym.name != 'time.Time' {
2290 mut sub := node.sub_structs[field.name] or { continue }
2291 mut where_expr := sub.where_expr as ast.InfixExpr
2292 mut ident := where_expr.right as ast.Ident
2293 primitive_type_index := g.table.find_type('orm.Primitive')
2294
2295 if primitive_type_index != 0 {
2296 if mut ident.info is ast.IdentVar {
2297 ident.info.typ = primitive_type_index
2298 }
2299 }
2300
2301 ident.name = array_get_call_code
2302 ident.obj = ast.Var{
2303 name: array_get_call_code
2304 }
2305 where_expr.right = ident
2306 sub.where_expr = where_expr
2307
2308 sub_result_var := g.new_tmp_var()
2309 sub_result_c_typ := g.styp(sub.typ)
2310 g.writeln('${sub_result_c_typ} ${sub_result_var};')
2311 g.write_orm_select(sub, connection_var_name, sub_result_var)
2312 if final_field_typ.has_flag(.option) {
2313 unwrapped_field_c_typ := g.styp(final_field_typ.clear_flag(.option))
2314 g.writeln('if (!${sub_result_var}.is_error)')
2315 g.writeln('\tbuiltin___option_ok(${sub_result_var}.data, (_option *)&${field_var}, sizeof(${unwrapped_field_c_typ}));')
2316 g.writeln('else')
2317 g.writeln('\t${field_var} = (${field_c_typ}){ .state = 2, .err = _const_none__, .data = {E_STRUCT} };')
2318 } else {
2319 g.writeln('if (!${sub_result_var}.is_error)')
2320 g.writeln('\t${field_var} = *(${field_c_typ}*)${sub_result_var}.data;')
2321 }
2322 fields_idx++
2323 } else if sym.kind == .array {
2324 mut fkey := ''
2325 if attr := field.attrs.find_first('fkey') {
2326 fkey = attr.arg
2327 } else {
2328 verror('missing fkey attribute')
2329 }
2330 sub := node.sub_structs[field.name] or { continue }
2331 if sub.has_where {
2332 mut where_expr := sub.where_expr as ast.InfixExpr
2333 mut left_where_expr := where_expr.left as ast.Ident
2334 mut right_where_expr := where_expr.right as ast.Ident
2335 left_where_expr.name = fkey
2336 right_where_expr.name = tmp
2337 right_where_expr.obj = ast.Var{
2338 name: tmp
2339 }
2340 where_expr.left = left_where_expr
2341 where_expr.right = ast.SelectorExpr{
2342 pos: right_where_expr.pos
2343 field_name: primary_field.name
2344 is_mut: false
2345 expr: right_where_expr
2346 expr_type: (right_where_expr.info as ast.IdentVar).typ
2347 typ: (right_where_expr.info as ast.IdentVar).typ
2348 scope: unsafe { nil }
2349 }
2350
2351 mut sql_expr_select_array := ast.SqlExpr{
2352 typ: final_field_typ.set_flag(.result)
2353 aggregate_kind: sub.aggregate_kind
2354 aggregate_field: sub.aggregate_field
2355 db_expr: sub.db_expr
2356 has_where: sub.has_where
2357 has_offset: sub.has_offset
2358 offset_expr: sub.offset_expr
2359 has_order: sub.has_order
2360 order_expr: sub.order_expr
2361 has_desc: sub.has_desc
2362 is_array: true
2363 is_generated: true
2364 pos: sub.pos
2365 has_limit: sub.has_limit
2366 limit_expr: sub.limit_expr
2367 table_expr: sub.table_expr
2368 fields: sub.fields
2369 sub_structs: sub.sub_structs
2370 where_expr: where_expr
2371 aggregate_field_type: sub.aggregate_field_type
2372 }
2373
2374 sub_result_var := g.new_tmp_var()
2375 sub_result_c_typ := g.styp(sub.typ)
2376 g.writeln('${sub_result_c_typ} ${sub_result_var};')
2377 g.write_orm_select(sql_expr_select_array, connection_var_name, sub_result_var)
2378 g.writeln('if (!${sub_result_var}.is_error) {')
2379 if final_field_typ.has_flag(.option) {
2380 g.writeln('\t${field_var}.state = 0;')
2381 g.writeln('\t*(${g.base_type(final_field_typ)}*)${field_var}.data = *(${g.base_type(final_field_typ)}*)${sub_result_var}.data;')
2382 } else {
2383 g.writeln('\t${field_var} = *(${g.base_type(field.typ)}*)${sub_result_var}.data;')
2384 }
2385 g.writeln('}')
2386 }
2387 } else if final_field_typ.has_flag(.option) {
2388 prim_var := g.new_tmp_var()
2389 g.writeln('orm__Primitive *${prim_var} = &${array_get_call_code};')
2390 g.writeln('if (${prim_var}->_typ == ${g.table.find_type_idx('orm.Null')})')
2391 g.writeln('\t${field_var} = (${field_c_typ}){ .state = 2, .err = _const_none__, .data = {E_STRUCT} };')
2392
2393 g.writeln('else')
2394 g.writeln('\tbuiltin___option_ok(${prim_var}->_${sym.cname}, (_option *)&${field_var}, sizeof(${sym.cname}));')
2395 fields_idx++
2396 } else if sym.kind == .enum {
2397 mut typ := sym.cname
2398 g.writeln('${tmp}.${orm_field_access_name(field.name)} = (${typ}) (*(${array_get_call_code}._i64));')
2399 fields_idx++
2400 } else {
2401 g.writeln('${field_var} = *(${array_get_call_code}._${sym.cname});')
2402 fields_idx++
2403 }
2404 }
2405
2406 if node.is_array {
2407 if node.typ.has_flag(.option) {
2408 g.writeln('${tmp}_array.state = 0;')
2409 g.writeln('builtin__array_push((${g.base_type(node.typ)}*)&${tmp}_array.data, _MOV((${typ_str}[]){ ${tmp} }));')
2410 } else {
2411 g.writeln('builtin__array_push(&${tmp}_array, _MOV((${typ_str}[]){ ${tmp} }));')
2412 }
2413 g.indent--
2414 g.writeln('}')
2415 }
2416
2417 g.indent--
2418 if !node.is_array {
2419 g.writeln('} else {')
2420 g.writeln('\t${result_var}.is_error = true;')
2421 }
2422 g.writeln('}')
2423
2424 if node.is_array {
2425 if node.typ.has_flag(.option) {
2426 g.writeln('*(${g.base_type(node.typ)}*) ${result_var}.data = *(${g.base_type(node.typ)}*)${tmp}_array.data;')
2427 } else {
2428 g.writeln('*(${unwrapped_c_typ}*) ${result_var}.data = ${tmp}_array;')
2429 }
2430 } else {
2431 g.writeln('*(${unwrapped_c_typ}*) ${result_var}.data = ${tmp};')
2432 }
2433 }
2434
2435 g.indent--
2436 g.writeln('}')
2437
2438 if node.is_generated {
2439 g.writeln(';')
2440 }
2441}
2442
2443// filter_struct_fields_by_orm_attrs filters struct fields taking into its attributes.
2444// Used by non-create queries for skipping fields.
2445fn (_ &Gen) filter_struct_fields_by_orm_attrs(fields []ast.StructField) []ast.StructField {
2446 mut ret := []ast.StructField{}
2447
2448 for field in fields {
2449 if field.attrs.contains('skip') || field.attrs.contains_arg('sql', '-') {
2450 continue
2451 }
2452 ret << field
2453 }
2454
2455 return ret
2456}
2457
2458// get_db_expr_type returns the database type from the database expression.
2459fn (g &Gen) get_db_expr_type(expr ast.Expr) ?ast.Type {
2460 if expr is ast.Ident {
2461 if expr.info is ast.IdentVar {
2462 return g.table.unaliased_type(expr.info.typ)
2463 }
2464 } else if expr is ast.SelectorExpr {
2465 return g.table.unaliased_type(expr.typ)
2466 }
2467
2468 return none
2469}
2470
2471// get_table_attrs_by_struct_type returns the struct attrs.
2472fn (g &Gen) get_table_attrs_by_struct_type(typ ast.Type) []ast.Attr {
2473 sym := g.table.sym(typ)
2474 info := sym.struct_info()
2475 return info.attrs
2476}
2477
2478// get_table_name_by_struct_type converts the struct type to a table name.
2479// For generic types, uses ngname (name without generic params) to get the base table name.
2480fn (g &Gen) get_table_name_by_struct_type(typ ast.Type) string {
2481 sym := g.table.sym(typ)
2482 info := sym.struct_info()
2483 // Use ngname for generic types to strip the generic parameters (e.g., Message[Payload] -> Message)
2484 // Fall back to stripping manually from name if ngname is empty
2485 base_name := if sym.ngname.len > 0 {
2486 sym.ngname
2487 } else {
2488 // Strip generic parameters manually (e.g., main.Message[main.Payload] -> main.Message)
2489 sym.name.all_before('[')
2490 }
2491 mut table_name := util.strip_mod_name(base_name)
2492
2493 if attr := info.attrs.find_first('table') {
2494 table_name = attr.arg
2495 } else {
2496 // Keep default ORM table names aligned with unquoted SQL identifiers across DB drivers.
2497 table_name = table_name.to_lower()
2498 }
2499 escaped_table_name := cescape_nonascii(util.smart_quote(table_name, false))
2500 return escaped_table_name
2501}
2502
2503// get_orm_current_table_field returns the current processing table's struct field by name.
2504fn (g &Gen) get_orm_current_table_field(name string) ?ast.StructField {
2505 // Use sql_table_typ directly for proper generic type support
2506 sym := g.table.sym(g.sql_table_typ)
2507 // For GenericInst types, get the struct info from the parent type
2508 info := if sym.info is ast.GenericInst {
2509 g.table.type_symbols[sym.info.parent_idx].struct_info()
2510 } else {
2511 sym.struct_info()
2512 }
2513
2514 for field in info.fields {
2515 if field.name == name {
2516 return field
2517 }
2518 }
2519
2520 return none
2521}
2522
2523// get_orm_column_name_from_struct_field converts the struct field to a table column name.
2524fn (g &Gen) get_orm_column_name_from_struct_field(field ast.StructField) string {
2525 mut name := field.name
2526
2527 if attr := field.attrs.find_first('sql') {
2528 if attr.arg !in ['serial', 'i8', 'i16', 'i32', 'int', 'i64', 'u8', 'u16', 'u32', 'u64',
2529 'f32', 'f64', 'bool', 'string'] {
2530 name = attr.arg
2531 }
2532 }
2533
2534 final_field_typ := g.table.final_type(field.typ)
2535 sym := g.table.sym(final_field_typ)
2536 if sym.kind == .struct && sym.name != 'time.Time' {
2537 name = '${name}_id'
2538 }
2539
2540 return name
2541}
2542
2543fn (g &Gen) get_orm_field_by_column_name(fields []ast.StructField, column string) ?ast.StructField {
2544 for field in fields {
2545 if g.get_orm_column_name_from_struct_field(field) == column {
2546 return field
2547 }
2548 }
2549 return none
2550}
2551
2552// get_orm_select_expr_from_struct_field returns the SQL expression used in a SELECT list.
2553fn (g &Gen) get_orm_select_expr_from_struct_field(field ast.StructField) string {
2554 if attr := field.attrs.find_first('sql_select') {
2555 arg := attr.arg.trim_space()
2556 if arg.len >= 2 && ((arg.starts_with("'") && arg.ends_with("'"))
2557 || (arg.starts_with('"') && arg.ends_with('"'))) {
2558 return arg[1..arg.len - 1].trim_space()
2559 }
2560 return arg
2561 }
2562 return g.get_orm_column_name_from_struct_field(field)
2563}
2564
2565// get_orm_struct_primary_field returns the table's primary column field.
2566fn (_ &Gen) get_orm_struct_primary_field(fields []ast.StructField) ?ast.StructField {
2567 for field in fields {
2568 if _ := field.attrs.find_first('primary') {
2569 return field
2570 }
2571 }
2572 return none
2573}
2574
2575fn (g &Gen) get_orm_upsert_conflict_groups(fields []ast.StructField, table_attrs []ast.Attr) [][]string {
2576 mut groups := [][]string{}
2577 mut named_unique_groups := map[string][]string{}
2578 mut named_unique_group_order := []string{}
2579 mut seen := map[string]bool{}
2580 for field in fields {
2581 column_name := g.get_orm_column_name_from_struct_field(field)
2582 for attr in field.attrs {
2583 match attr.name {
2584 'primary' {
2585 key := column_name
2586 if key !in seen {
2587 groups << [column_name]
2588 seen[key] = true
2589 }
2590 }
2591 'unique' {
2592 if attr.arg != '' && attr.kind == .string {
2593 if attr.arg !in named_unique_groups {
2594 named_unique_groups[attr.arg] = []string{}
2595 named_unique_group_order << attr.arg
2596 }
2597 named_unique_groups[attr.arg] << column_name
2598 } else {
2599 key := column_name
2600 if key !in seen {
2601 groups << [column_name]
2602 seen[key] = true
2603 }
2604 }
2605 }
2606 else {}
2607 }
2608 }
2609 }
2610 for group_name in named_unique_group_order {
2611 group := named_unique_groups[group_name]
2612 key := group.join(',')
2613 if key !in seen {
2614 groups << group
2615 seen[key] = true
2616 }
2617 }
2618 for attr in table_attrs {
2619 if attr.name != 'unique_key' || attr.arg == '' || attr.kind != .string {
2620 continue
2621 }
2622 mut group := []string{}
2623 for raw_field_name in attr.arg.split(',') {
2624 field_name := raw_field_name.trim_space()
2625 if field_name != '' {
2626 group << field_name
2627 }
2628 }
2629 key := group.join(',')
2630 if group.len > 0 && key !in seen {
2631 groups << group
2632 seen[key] = true
2633 }
2634 }
2635 return groups
2636}
2637
2638fn (mut g Gen) write_orm_upsert_conflict_groups(groups [][]string) {
2639 if groups.len == 0 {
2640 g.write('builtin____new_array_with_default_noscan(0, 0, sizeof(Array_string), 0)')
2641 return
2642 }
2643 g.write('builtin__new_array_from_c_array(${groups.len}, ${groups.len}, sizeof(Array_string), _MOV((Array_string[${groups.len}]){')
2644 for group in groups {
2645 if group.len == 0 {
2646 g.write('builtin____new_array_with_default_noscan(0, 0, sizeof(string), 0),')
2647 continue
2648 }
2649 g.write('builtin__new_array_from_c_array(${group.len}, ${group.len}, sizeof(string), _MOV((string[${group.len}]){')
2650 for field_name in group {
2651 g.write('_S("${field_name}"),')
2652 }
2653 g.write('})),')
2654 }
2655 g.write('}))') // closing: `}` compound literal init, `)` _MOV, `)` new_array_from_c_array
2656}
2657
2658// return indexes of any auto-increment fields or fields with default values
2659fn get_auto_field_idxs(fields []ast.StructField) []int {
2660 mut ret := []int{}
2661 for i, field in fields {
2662 for attr in field.attrs {
2663 if attr.name == 'default' {
2664 ret << i
2665 } else if attr.name == 'sql' && attr.arg == 'serial' {
2666 ret << i
2667 } else if attr.name == 'serial' && attr.kind == .plain && !attr.has_arg {
2668 ret << i
2669 }
2670 }
2671 }
2672 return ret
2673}
2674
2675// write_orm_joins writes C code for the joins array in SelectConfig
2676fn (mut g Gen) write_orm_joins(joins []ast.JoinClause) {
2677 if joins.len == 0 {
2678 g.writeln('.joins = builtin____new_array_with_default_noscan(0, 0, sizeof(orm__JoinConfig), 0),')
2679 return
2680 }
2681
2682 g.writeln('.joins = builtin__new_array_from_c_array(${joins.len}, ${joins.len}, sizeof(orm__JoinConfig),')
2683 g.indent++
2684 g.writeln('_MOV((orm__JoinConfig[${joins.len}]){')
2685 g.indent++
2686
2687 for join in joins {
2688 g.writeln('(orm__JoinConfig){')
2689 g.indent++
2690
2691 // Write join kind
2692 kind_str := match join.kind {
2693 .inner { 'orm__JoinType__inner' }
2694 .left { 'orm__JoinType__left' }
2695 .right { 'orm__JoinType__right' }
2696 .full_outer { 'orm__JoinType__full_outer' }
2697 }
2698
2699 g.writeln('.kind = ${kind_str},')
2700
2701 // Write joined table info
2702 g.write('.table = ')
2703 g.write_orm_table_struct(join.table_expr.typ)
2704 g.writeln(',')
2705
2706 // Extract column names from the ON expression (should be an InfixExpr)
2707 left_col, right_col := g.extract_join_columns(join.on_expr)
2708 g.writeln('.on_left_col = _S("${left_col}"),')
2709 g.writeln('.on_right_col = _S("${right_col}"),')
2710
2711 g.indent--
2712 g.writeln('},')
2713 }
2714
2715 g.indent--
2716 g.writeln('})')
2717 g.indent--
2718 g.writeln('),')
2719}
2720
2721// extract_join_columns extracts the left and right column names from a JOIN ON expression.
2722// The ON expression is expected to be an InfixExpr like: User.dept_id == Department.id
2723// Returns (left_col, right_col) where left_col is from the main table, right_col is from the joined table.
2724fn (g &Gen) extract_join_columns(on_expr ast.Expr) (string, string) {
2725 if on_expr is ast.InfixExpr {
2726 left_col := g.extract_join_field_name(on_expr.left)
2727 right_col := g.extract_join_field_name(on_expr.right)
2728 return left_col, right_col
2729 }
2730
2731 // Fallback: return empty strings if the expression is not the expected format
2732 return '', ''
2733}
2734
2735// extract_join_field_name extracts a field name from a JOIN ON expression operand.
2736// Handles both SelectorExpr (Table.field) and EnumVal (when parser interprets Type.field as enum).
2737fn (g &Gen) extract_join_field_name(expr ast.Expr) string {
2738 if expr is ast.SelectorExpr {
2739 return expr.field_name
2740 }
2741 if expr is ast.EnumVal {
2742 // EnumVal.val contains the field name (e.g., "department_id" from "User.department_id")
2743 return expr.val
2744 }
2745 return ''
2746}
2747