vxx2 / vlib / v / parser / orm.v
925 lines · 864 sloc · 22.32 KB · 27f394507e26c13f3d1bd3a8868f7b649038e2fd
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module parser
5
6import v.ast
7import v.token
8
9struct SqlPrefix {
10 pos token.Pos
11 db_expr ast.Expr
12 tmp_inside_match bool
13 is_dynamic bool
14}
15
16struct SqlQueryDataItemsBlock {
17 items []ast.SqlQueryDataItem
18 end_comments []ast.Comment
19}
20
21fn sql_aggregate_kind_from_name(name string) ast.SqlAggregateKind {
22 return match name {
23 'count' { .count }
24 'sum' { .sum }
25 'avg' { .avg }
26 'min' { .min }
27 'max' { .max }
28 else { .none }
29 }
30}
31
32fn (mut p Parser) parse_sql_select_field_name() ast.SqlSelectField {
33 pos := p.tok.pos()
34 mut parts := []string{}
35 parts << p.check_name()
36 for p.tok.kind == .dot {
37 p.next()
38 parts << p.check_name()
39 }
40 return ast.SqlSelectField{
41 name: parts.join('.')
42 pos: pos.extend(p.prev_tok.pos())
43 }
44}
45
46fn (mut p Parser) parse_sql_select_fields() []ast.SqlSelectField {
47 mut fields := []ast.SqlSelectField{}
48 for {
49 if p.tok.kind != .name {
50 p.error('expected an ORM select field before `from`')
51 return fields
52 }
53 fields << p.parse_sql_select_field_name()
54 if p.tok.kind != .comma {
55 break
56 }
57 p.next()
58 }
59 return fields
60}
61
62// select from User
63// insert user into User returning id
64fn (mut p Parser) sql_expr() ast.Expr {
65 prefix := p.sql_prefix()
66 return p.sql_expr_after_prefix(prefix)
67}
68
69fn (mut p Parser) sql_stmt_or_expr() ast.Stmt {
70 prefix := p.sql_prefix()
71 if p.sql_stmt_can_be_value()
72 && (p.tok.kind == .key_select || (p.tok.kind == .name && p.tok.lit == 'insert')) {
73 expr := p.sql_expr_after_prefix(prefix)
74 return ast.ExprStmt{
75 expr: expr
76 pos: expr.pos()
77 }
78 }
79 return p.sql_stmt_after_prefix(prefix)
80}
81
82fn (p &Parser) sql_stmt_can_be_value() bool {
83 return p.inside_if_expr || p.inside_match_body || p.inside_or_expr
84}
85
86fn (mut p Parser) sql_prefix() SqlPrefix {
87 tmp_inside_match := p.inside_match
88 p.inside_orm = true
89 p.inside_match = true
90 pos := p.tok.pos()
91 p.check_name()
92 db_expr := p.check_expr(0) or {
93 p.unexpected(prepend_msg: 'invalid expression:', expecting: 'database')
94 }
95 p.check(.lcbr)
96 mut is_dynamic := false
97 if p.tok.kind == .name && p.tok.lit == 'dynamic' {
98 is_dynamic = true
99 p.next()
100 }
101 return SqlPrefix{
102 pos: pos
103 db_expr: db_expr
104 tmp_inside_match: tmp_inside_match
105 is_dynamic: is_dynamic
106 }
107}
108
109fn (mut p Parser) sql_expr_after_prefix(prefix SqlPrefix) ast.Expr {
110 is_dynamic := prefix.is_dynamic
111 is_select := p.tok.kind == .key_select
112 is_insert := p.tok.lit == 'insert'
113 if !is_select && !is_insert {
114 p.error('expected "select" or "insert" in an ORM expression')
115 }
116 if is_dynamic && !is_select {
117 p.error('expected "select" after `dynamic` in an ORM expression')
118 }
119 p.next()
120 // kind := if is_select { ast.SqlExprKind.select_ } else { ast.SqlExprKind.insert }
121 mut inserted_var := ''
122 mut aggregate_kind := ast.SqlAggregateKind.none
123 mut aggregate_field := ''
124 mut has_distinct := false
125 mut requested_fields := []ast.SqlSelectField{}
126 if is_insert {
127 inserted_var = p.check_name()
128 p.scope.mark_var_as_used(inserted_var)
129 into := p.check_name()
130 if into != 'into' {
131 p.error('expecting `into`')
132 }
133 } else if is_select {
134 if p.tok.kind == .name && p.tok.lit == 'distinct' {
135 has_distinct = true
136 p.next()
137 }
138 if p.tok.kind == .name {
139 aggregate_kind = sql_aggregate_kind_from_name(p.tok.lit)
140 }
141 if aggregate_kind == .count {
142 p.next()
143 n := p.check_name() // from
144 if n != 'from' {
145 p.error('expecting "from" in a "select count" ORM statement')
146 }
147 } else if aggregate_kind != .none {
148 p.next()
149 if p.tok.kind != .lpar {
150 p.error('expecting `(` after aggregate function in ORM select')
151 }
152 p.next()
153 if p.tok.kind != .name {
154 p.error('ORM aggregate functions only support a single field name argument')
155 }
156 aggregate_field = p.check_name()
157 if p.tok.kind != .rpar {
158 p.error('ORM aggregate functions only support a single field name argument')
159 }
160 p.next()
161 n := p.check_name() // from
162 if n != 'from' {
163 p.error('expecting `from` after ORM aggregate function')
164 }
165 } else if p.tok.kind == .name && p.tok.lit == 'from' {
166 p.next()
167 } else {
168 requested_fields = p.parse_sql_select_fields()
169 n := p.check_name() // from
170 if n != 'from' {
171 p.error('expecting `from` after ORM select fields')
172 }
173 }
174 }
175 mut typ := ast.void_type
176
177 table_pos := p.tok.pos()
178 table_type := p.parse_type() // `User`
179
180 // Parse JOIN clauses (e.g., `join Department on User.dept_id == Department.id`)
181 mut joins := []ast.JoinClause{}
182 for p.tok.kind == .name && p.tok.lit in ['join', 'left', 'right', 'full', 'inner'] {
183 join_clause := p.parse_sql_join_clause()
184 if join_clause.table_expr.typ != ast.void_type {
185 joins << join_clause
186 }
187 }
188
189 mut where_expr := ast.empty_expr
190 has_where := p.tok.kind == .name && p.tok.lit == 'where'
191
192 if has_where {
193 p.next()
194 where_expr = p.expr(0)
195 if !is_dynamic {
196 where_check_result := p.check_sql_where_expr_has_no_undefined_variables(&where_expr, [])
197 if where_check_result is ast.NodeError {
198 return where_check_result
199 }
200 }
201 }
202
203 mut has_limit := false
204 mut limit_expr := ast.empty_expr
205 mut has_offset := false
206 mut offset_expr := ast.empty_expr
207 mut has_order := false
208 mut order_expr := ast.empty_expr
209 mut has_desc := false
210 if p.tok.kind == .name && p.tok.lit == 'order' {
211 p.check_name() // `order`
212 order_pos := p.tok.pos()
213 if p.tok.kind == .name && p.tok.lit == 'by' {
214 p.check_name() // `by`
215 } else {
216 return p.error_with_pos('use `order by` in ORM queries', order_pos)
217 }
218 has_order = true
219 order_expr = p.expr(0)
220 if p.tok.kind == .name && p.tok.lit in ['asc', 'desc'] {
221 has_desc = p.tok.lit == 'desc'
222 p.check_name() // `asc` or `desc`
223 }
224 }
225
226 if p.tok.kind == .name && p.tok.lit == 'limit' {
227 p.check_name() // `limit`
228 has_limit = true
229 limit_expr = p.expr(0)
230 }
231
232 if p.tok.kind == .name && p.tok.lit == 'offset' {
233 p.check_name() // `offset`
234 has_offset = true
235 offset_expr = p.expr(0)
236 }
237
238 if aggregate_kind == .count {
239 typ = ast.int_type
240 } else if aggregate_kind != .none {
241 typ = ast.void_type
242 } else if table_type.has_flag(.generic) {
243 typ = ast.new_type(p.table.find_or_register_array(table_type)).set_flag(.generic)
244 } else {
245 typ = ast.new_type(p.table.find_or_register_array(table_type))
246 }
247
248 p.check(.rcbr)
249 p.inside_match = false
250 p.inside_orm = false
251 or_expr := p.parse_sql_or_block()
252 p.inside_match = prefix.tmp_inside_match
253
254 return ast.SqlExpr{
255 aggregate_kind: aggregate_kind
256 aggregate_field: aggregate_field
257 is_insert: is_insert
258 typ: typ.set_flag(.result)
259 scope: p.scope
260 or_expr: or_expr
261 db_expr: prefix.db_expr
262 where_expr: where_expr
263 has_where: has_where
264 has_limit: has_limit
265 limit_expr: limit_expr
266 has_offset: has_offset
267 offset_expr: offset_expr
268 has_order: has_order
269 order_expr: order_expr
270 has_desc: has_desc
271 has_distinct: has_distinct
272 is_array: aggregate_kind == .none
273 is_generated: false
274 is_dynamic: is_dynamic
275 inserted_var: inserted_var
276 requested_fields: requested_fields
277 pos: prefix.pos.extend(p.prev_tok.pos())
278 table_expr: ast.TypeNode{
279 typ: table_type
280 pos: table_pos
281 }
282 joins: joins
283 }
284}
285
286// parse_sql_join_clause parses a JOIN clause like:
287// `join Department on User.dept_id == Department.id`
288// `left join Department on User.dept_id == Department.id`
289// `inner join Department on User.dept_id == Department.id`
290fn (mut p Parser) parse_sql_join_clause() ast.JoinClause {
291 mut kind := ast.JoinKind.inner
292 join_pos := p.tok.pos()
293
294 // Check for join type prefix (left, right, full, inner)
295 if p.tok.lit == 'left' {
296 kind = .left
297 p.next()
298 } else if p.tok.lit == 'right' {
299 kind = .right
300 p.next()
301 } else if p.tok.lit == 'full' {
302 kind = .full_outer
303 p.next()
304 // Handle optional 'outer' keyword
305 if p.tok.kind == .name && p.tok.lit == 'outer' {
306 p.next()
307 }
308 } else if p.tok.lit == 'inner' {
309 // 'inner' is optional, just skip it
310 p.next()
311 }
312
313 // Now expect 'join'
314 if p.tok.kind != .name || p.tok.lit != 'join' {
315 p.error('expected `join` keyword after join type')
316 return ast.JoinClause{}
317 }
318 p.next() // consume 'join'
319
320 // Parse the joined table type
321 table_pos := p.tok.pos()
322 table_type := p.parse_type()
323
324 // Expect 'on' keyword
325 if p.tok.kind != .name || p.tok.lit != 'on' {
326 p.error('expected `on` keyword after table name in JOIN clause')
327 return ast.JoinClause{}
328 }
329 p.next() // consume 'on'
330
331 // Parse the ON condition expression
332 on_expr := p.expr(0)
333
334 return ast.JoinClause{
335 kind: kind
336 pos: join_pos
337 table_expr: ast.TypeNode{
338 typ: table_type
339 pos: table_pos
340 }
341 on_expr: on_expr
342 }
343}
344
345// insert user into User
346// update User set nr_oders=nr_orders+1 where id == user_id
347// delete
348fn (mut p Parser) sql_stmt() ast.SqlStmt {
349 prefix := p.sql_prefix()
350 return p.sql_stmt_after_prefix(prefix)
351}
352
353fn (mut p Parser) sql_stmt_after_prefix(prefix SqlPrefix) ast.SqlStmt {
354 mut pos := prefix.pos
355
356 mut lines := []ast.SqlStmtLine{}
357
358 for p.tok.kind != .rcbr {
359 if p.tok.kind == .eof {
360 p.unexpected_with_pos(pos, got: 'eof, while parsing an SQL statement')
361 return ast.SqlStmt{}
362 }
363 lines << p.parse_sql_stmt_line(prefix.is_dynamic)
364 }
365
366 p.next()
367
368 p.inside_orm = false
369 p.inside_match = false
370 mut or_expr := p.parse_sql_or_block()
371 p.inside_match = prefix.tmp_inside_match
372
373 pos.last_line = p.prev_tok.line_nr
374 return ast.SqlStmt{
375 pos: pos.extend(p.prev_tok.pos())
376 db_expr: prefix.db_expr
377 lines: lines
378 or_expr: or_expr
379 }
380}
381
382fn (mut p Parser) parse_sql_or_block() ast.OrExpr {
383 mut stmts := []ast.Stmt{}
384 mut kind := ast.OrKind.absent
385 mut pos := p.tok.pos()
386 mut or_scope := ast.empty_scope
387
388 if p.tok.kind == .key_orelse {
389 kind = .block
390 stmts, pos, or_scope = p.or_block(.with_err_var)
391 } else if p.tok.kind == .not {
392 kind = .propagate_result
393 or_scope = p.scope
394 p.next()
395 }
396
397 return ast.OrExpr{
398 stmts: stmts
399 kind: kind
400 pos: pos
401 scope: or_scope
402 }
403}
404
405fn (mut p Parser) parse_sql_stmt_line(is_dynamic bool) ast.SqlStmtLine {
406 pre_comments := p.eat_comments()
407 mut n := p.check_name() // insert
408 pos := p.tok.pos()
409 mut kind := ast.SqlStmtKind.insert
410 if n == 'delete' {
411 kind = .delete
412 } else if n == 'upsert' {
413 kind = .upsert
414 } else if n == 'update' {
415 kind = .update
416 } else if n == 'create' {
417 kind = .create
418 table := p.check_name()
419 if table != 'table' {
420 p.error('expected `table` got `${table}`')
421 return ast.SqlStmtLine{}
422 }
423 typ := p.parse_type()
424 typ_pos := p.tok.pos()
425 end_comments := p.eat_comments()
426 return ast.SqlStmtLine{
427 kind: kind
428 pos: pos.extend(p.prev_tok.pos())
429 table_expr: ast.TypeNode{
430 typ: typ
431 pos: typ_pos
432 }
433 scope: p.scope
434 is_generated: false
435 is_dynamic: is_dynamic
436 pre_comments: pre_comments
437 end_comments: end_comments
438 }
439 } else if n == 'drop' {
440 kind = .drop
441 table := p.check_name()
442 if table != 'table' {
443 p.error('expected `table` got `${table}`')
444 return ast.SqlStmtLine{}
445 }
446 typ := p.parse_type()
447 typ_pos := p.tok.pos()
448 end_comments := p.eat_comments()
449 return ast.SqlStmtLine{
450 kind: kind
451 pos: pos.extend(p.prev_tok.pos())
452 table_expr: ast.TypeNode{
453 typ: typ
454 pos: typ_pos
455 }
456 is_generated: false
457 is_dynamic: is_dynamic
458 scope: p.scope
459 pre_comments: pre_comments
460 end_comments: end_comments
461 }
462 }
463 if is_dynamic && kind != .update {
464 p.error('`dynamic` is only supported for ORM `select` and `update` queries')
465 return ast.SqlStmtLine{}
466 }
467 mut inserted_var := ''
468 mut table_type := ast.no_type
469 if kind != .delete {
470 if kind == .update {
471 table_type = p.parse_type()
472 } else if kind in [.insert, .upsert] {
473 expr := p.expr(0)
474 if expr is ast.Ident {
475 inserted_var = expr.name
476 } else {
477 p.error('can only insert variables')
478 return ast.SqlStmtLine{}
479 }
480 }
481 }
482 n = p.check_name() // into
483 mut updated_columns := []string{}
484 mut update_exprs := []ast.Expr{cap: 5}
485 mut update_data_expr := ast.empty_expr
486 if kind in [.insert, .upsert] && n != 'into' {
487 p.error('expecting `into`')
488 return ast.SqlStmtLine{}
489 } else if kind == .update {
490 if n != 'set' {
491 p.error('expecting `set`')
492 return ast.SqlStmtLine{}
493 }
494 if is_dynamic {
495 update_data_expr = p.expr(0)
496 } else {
497 for {
498 column := p.check_name()
499 updated_columns << column
500 p.check(.assign)
501 update_exprs << p.expr(0)
502 if p.tok.kind == .comma {
503 p.check(.comma)
504 } else {
505 break
506 }
507 }
508 }
509 } else if kind == .delete && n != 'from' {
510 p.error('expecting `from`')
511 return ast.SqlStmtLine{}
512 }
513
514 mut table_pos := p.tok.pos()
515 mut where_expr := ast.empty_expr
516 if kind in [.insert, .upsert] {
517 table_pos = p.tok.pos()
518 table_type = p.parse_type()
519 } else if kind == .update {
520 p.check_sql_keyword('where') or { return ast.SqlStmtLine{} }
521 where_expr = p.expr(0)
522
523 where_expr_result := p.check_sql_where_expr_has_no_undefined_variables(&where_expr, [])
524 if where_expr_result is ast.NodeError {
525 return ast.SqlStmtLine{}
526 }
527 } else if kind == .delete {
528 table_pos = p.tok.pos()
529 table_type = p.parse_type()
530 p.check_sql_keyword('where') or { return ast.SqlStmtLine{} }
531 where_expr = p.expr(0)
532
533 where_expr_result := p.check_sql_where_expr_has_no_undefined_variables(&where_expr, [])
534 if where_expr_result is ast.NodeError {
535 return ast.SqlStmtLine{}
536 }
537 }
538 end_comments := p.eat_comments()
539 return ast.SqlStmtLine{
540 table_expr: ast.TypeNode{
541 typ: table_type
542 pos: table_pos
543 }
544 object_var: inserted_var
545 pos: pos
546 updated_columns: updated_columns
547 update_exprs: update_exprs
548 update_data_expr: update_data_expr
549 kind: kind
550 where_expr: where_expr
551 is_generated: false
552 is_dynamic: is_dynamic
553 scope: p.scope
554 pre_comments: pre_comments
555 end_comments: end_comments
556 }
557}
558
559fn (p &Parser) is_sql_query_data_expr() bool {
560 if p.tok.kind != .lcbr {
561 return false
562 }
563 mut idx := 1
564 for p.peek_token(idx).kind == .comment {
565 idx++
566 }
567 mut first := p.peek_token(idx)
568 if first.kind == .key_if {
569 return true
570 }
571 has_leading_paren := first.kind == .lpar
572 if has_leading_paren && p.sql_query_data_item_has_top_level_colon(idx) {
573 return false
574 }
575 mut leading_parens := 0
576 for first.kind == .lpar {
577 leading_parens++
578 idx++
579 for p.peek_token(idx).kind == .comment {
580 idx++
581 }
582 first = p.peek_token(idx)
583 }
584 if first.kind != .name {
585 return false
586 }
587 idx++
588 for p.peek_token(idx).kind == .dot && p.peek_token(idx + 1).kind == .name {
589 idx += 2
590 }
591 for leading_parens > 0 && p.peek_token(idx).kind == .rpar {
592 leading_parens--
593 idx++
594 for p.peek_token(idx).kind == .comment {
595 idx++
596 }
597 }
598 return is_sql_query_data_operator(p.peek_token(idx).kind)
599}
600
601fn (p &Parser) sql_query_data_item_has_top_level_colon(start_idx int) bool {
602 mut idx := start_idx
603 mut par_depth := 0
604 mut bracket_depth := 0
605 mut brace_depth := 0
606 for {
607 kind := p.peek_token(idx).kind
608 match kind {
609 .eof {
610 return false
611 }
612 .comment {}
613 .lpar {
614 par_depth++
615 }
616 .rpar {
617 if par_depth > 0 {
618 par_depth--
619 }
620 }
621 .lsbr {
622 bracket_depth++
623 }
624 .rsbr {
625 if bracket_depth > 0 {
626 bracket_depth--
627 }
628 }
629 .lcbr {
630 brace_depth++
631 }
632 .rcbr {
633 if brace_depth == 0 {
634 return false
635 }
636 brace_depth--
637 }
638 .comma {
639 if par_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
640 return false
641 }
642 }
643 .colon {
644 if par_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
645 return true
646 }
647 }
648 else {}
649 }
650
651 idx++
652 }
653 return false
654}
655
656fn is_sql_query_data_operator(kind token.Kind) bool {
657 return kind in [.eq, .ne, .gt, .lt, .ge, .le, .key_like, .key_ilike, .key_in, .not_in, .key_is,
658 .not_is]
659}
660
661fn (mut p Parser) sql_query_data_expr() ast.Expr {
662 start_pos := p.tok.pos()
663 block := p.parse_sql_query_data_items_block()
664 return ast.SqlQueryDataExpr{
665 pos: start_pos.extend(p.prev_tok.pos())
666 items: block.items
667 end_comments: block.end_comments
668 }
669}
670
671fn (mut p Parser) parse_sql_query_data_items_block() SqlQueryDataItemsBlock {
672 p.check(.lcbr)
673 mut items := []ast.SqlQueryDataItem{}
674 mut pending_comments := p.eat_comments()
675 for {
676 if p.tok.kind == .rcbr {
677 break
678 }
679 if p.tok.kind == .eof {
680 p.unexpected(got: 'eof, while parsing a dynamic ORM expression')
681 break
682 }
683 mut item := p.parse_sql_query_data_item()
684 item = sql_query_data_item_with_pre_comments(item, pending_comments)
685 pending_comments = []
686 item = sql_query_data_item_with_end_comments(item, p.eat_comments(same_line: true))
687 if p.tok.kind == .comma {
688 p.next()
689 item = sql_query_data_item_with_end_comments(item, p.eat_comments(same_line: true))
690 items << item
691 pending_comments = p.eat_comments()
692 continue
693 }
694 items << item
695 pending_comments = p.eat_comments()
696 if p.tok.kind != .rcbr {
697 p.error('expected `,` or `}` in a dynamic ORM expression')
698 break
699 }
700 }
701 p.check(.rcbr)
702 return SqlQueryDataItemsBlock{
703 items: items
704 end_comments: pending_comments
705 }
706}
707
708fn (mut p Parser) parse_sql_query_data_item() ast.SqlQueryDataItem {
709 if p.tok.kind == .key_if {
710 return p.parse_sql_query_data_if_item()
711 }
712 expr := p.expr(0)
713 return ast.SqlQueryDataLeaf{
714 pos: expr.pos()
715 expr: expr
716 }
717}
718
719fn sql_query_data_item_with_pre_comments(item ast.SqlQueryDataItem, comments []ast.Comment) ast.SqlQueryDataItem {
720 if comments.len == 0 {
721 return item
722 }
723 return match item {
724 ast.SqlQueryDataLeaf {
725 mut leaf := item
726 leaf.pre_comments << comments
727 leaf
728 }
729 ast.SqlQueryDataIf {
730 mut if_item := item
731 if_item.pre_comments << comments
732 if_item
733 }
734 }
735}
736
737fn sql_query_data_item_with_end_comments(item ast.SqlQueryDataItem, comments []ast.Comment) ast.SqlQueryDataItem {
738 if comments.len == 0 {
739 return item
740 }
741 return match item {
742 ast.SqlQueryDataLeaf {
743 mut leaf := item
744 leaf.end_comments << comments
745 leaf
746 }
747 ast.SqlQueryDataIf {
748 mut if_item := item
749 if_item.end_comments << comments
750 if_item
751 }
752 }
753}
754
755fn (mut p Parser) parse_sql_query_data_if_guard_expr() ast.IfGuardExpr {
756 p.open_scope()
757 mut vars := []ast.IfGuardVar{}
758 mut var_names := []string{}
759 for {
760 mut var := ast.IfGuardVar{}
761 mut is_mut := false
762 if p.tok.kind == .key_mut {
763 is_mut = true
764 p.next()
765 }
766 var.is_mut = p.scope_var_is_mut(is_mut)
767 var.pos = p.tok.pos()
768 var.name = p.check_name()
769 var_names << var.name
770
771 if p.scope.known_var(var.name) {
772 p.error_with_pos('redefinition of `${var.name}`', var.pos)
773 }
774 vars << var
775 if p.tok.kind != .comma {
776 break
777 }
778 p.next()
779 }
780 p.check(.decl_assign)
781 old_assign_rhs := p.inside_assign_rhs
782 p.inside_assign_rhs = true
783 expr := p.expr(0)
784 p.inside_assign_rhs = old_assign_rhs
785 if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr, ast.SelectorExpr, ast.Ident] {
786 p.error_with_pos('if guard condition expression is illegal, it should return an Option',
787 expr.pos())
788 }
789 p.check_undefined_variables(var_names, expr) or { p.error_with_pos(err.msg(), expr.pos()) }
790 cond := ast.IfGuardExpr{
791 vars: vars
792 expr: expr
793 }
794 for var in vars {
795 p.scope.register(ast.Var{
796 name: var.name
797 is_mut: var.is_mut
798 expr: cond
799 pos: var.pos
800 })
801 }
802 return cond
803}
804
805fn (mut p Parser) parse_sql_query_data_if_item() ast.SqlQueryDataItem {
806 start_pos := p.tok.pos()
807 mut branches := []ast.SqlQueryDataBranch{}
808 mut has_else := false
809 mut prev_guard := false
810 for {
811 branch_pos := p.tok.pos()
812 p.check(.key_if)
813 mut cond := ast.empty_expr
814 mut has_guard_scope := false
815 if p.peek_token_after_var_list().kind == .decl_assign {
816 cond = p.parse_sql_query_data_if_guard_expr()
817 has_guard_scope = true
818 prev_guard = true
819 } else {
820 cond = p.expr(0)
821 prev_guard = false
822 }
823 p.open_scope()
824 block := p.parse_sql_query_data_items_block()
825 p.close_scope()
826 if has_guard_scope {
827 p.close_scope()
828 }
829 branches << ast.SqlQueryDataBranch{
830 pos: branch_pos.extend(p.prev_tok.pos())
831 cond: cond
832 items: block.items
833 end_comments: block.end_comments
834 }
835 comments_before_else := p.eat_comments()
836 if p.tok.kind != .key_else {
837 return ast.SqlQueryDataIf{
838 pos: start_pos.extend(p.prev_tok.pos())
839 branches: branches
840 has_else: has_else
841 end_comments: comments_before_else
842 }
843 }
844 if comments_before_else.len > 0 {
845 last_idx := branches.len - 1
846 branches[last_idx].end_comments << comments_before_else
847 }
848 has_else = true
849 else_pos := p.tok.pos()
850 p.next()
851 if p.tok.kind == .key_if {
852 continue
853 }
854 p.open_scope()
855 if prev_guard {
856 p.scope.register(ast.Var{
857 name: 'err'
858 typ: ast.error_type
859 pos: p.tok.pos()
860 is_used: false
861 is_stack_obj: true
862 is_special: true
863 })
864 }
865 else_block := p.parse_sql_query_data_items_block()
866 p.close_scope()
867 branches << ast.SqlQueryDataBranch{
868 pos: else_pos.extend(p.prev_tok.pos())
869 cond: ast.empty_expr
870 items: else_block.items
871 end_comments: else_block.end_comments
872 }
873 break
874 }
875 return ast.SqlQueryDataIf{
876 pos: start_pos.extend(p.prev_tok.pos())
877 branches: branches
878 has_else: has_else
879 }
880}
881
882fn (mut p Parser) check_sql_keyword(name string) ?bool {
883 if p.check_name() != name {
884 p.error('ORM: expecting `${name}`')
885 return none
886 }
887 return true
888}
889
890// check_sql_where_expr_has_no_undefined_variables recursively tries to find undefined variables in the right part of infix expressions.
891fn (mut p Parser) check_sql_where_expr_has_no_undefined_variables(expr &ast.Expr, unacceptable_variable_names []string) ast.Expr {
892 if expr is ast.Ident {
893 if !p.scope.known_var(expr.name) {
894 p.check_undefined_variables(unacceptable_variable_names, expr) or {
895 return p.error_with_pos(err.msg(), expr.pos)
896 }
897 }
898 } else if expr is ast.InfixExpr {
899 if expr.left is ast.Ident {
900 if expr.right is ast.Ident {
901 return p.check_sql_where_expr_has_no_undefined_variables(expr.right, [
902 expr.left.name,
903 ])
904 }
905 }
906
907 left_check_result := p.check_sql_where_expr_has_no_undefined_variables(expr.left, [])
908
909 if left_check_result is ast.NodeError {
910 return left_check_result
911 }
912
913 variable_names := if expr.left is ast.Ident { [expr.left.name] } else { []string{} }
914 right_check_result := p.check_sql_where_expr_has_no_undefined_variables(expr.right,
915 variable_names)
916
917 if right_check_result is ast.NodeError {
918 return right_check_result
919 }
920 } else if expr is ast.ParExpr {
921 return p.check_sql_where_expr_has_no_undefined_variables(expr.expr, [])
922 }
923
924 return ast.empty_expr
925}
926