aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/v/parser/sql.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/v/parser/sql.v')
-rw-r--r--v_windows/v/vlib/v/parser/sql.v262
1 files changed, 262 insertions, 0 deletions
diff --git a/v_windows/v/vlib/v/parser/sql.v b/v_windows/v/vlib/v/parser/sql.v
new file mode 100644
index 0000000..28a7157
--- /dev/null
+++ b/v_windows/v/vlib/v/parser/sql.v
@@ -0,0 +1,262 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module parser
+
+import v.ast
+
+fn (mut p Parser) sql_expr() ast.Expr {
+ // `sql db {`
+ pos := p.tok.position()
+ p.check_name()
+ db_expr := p.check_expr(0) or {
+ p.error_with_pos('invalid expression: unexpected $p.tok, expecting database',
+ p.tok.position())
+ }
+ p.check(.lcbr)
+ p.check(.key_select)
+ n := p.check_name()
+ is_count := n == 'count'
+ mut typ := ast.void_type
+ if is_count {
+ p.check_name() // from
+ typ = ast.int_type
+ }
+ table_pos := p.tok.position()
+ table_type := p.parse_type() // `User`
+ mut where_expr := ast.empty_expr()
+ has_where := p.tok.kind == .name && p.tok.lit == 'where'
+ mut query_one := false // one object is returned, not an array
+ if has_where {
+ p.next()
+ where_expr = p.expr(0)
+ // `id == x` means that a single object is returned
+ if !is_count && where_expr is ast.InfixExpr {
+ e := where_expr as ast.InfixExpr
+ if e.op == .eq && e.left is ast.Ident {
+ if e.left.name == 'id' {
+ query_one = true
+ }
+ }
+ }
+ }
+ mut has_limit := false
+ mut limit_expr := ast.empty_expr()
+ mut has_offset := false
+ mut offset_expr := ast.empty_expr()
+ mut has_order := false
+ mut order_expr := ast.empty_expr()
+ mut has_desc := false
+ if p.tok.kind == .name && p.tok.lit == 'order' {
+ p.check_name() // `order`
+ order_pos := p.tok.position()
+ if p.tok.kind == .name && p.tok.lit == 'by' {
+ p.check_name() // `by`
+ } else {
+ return p.error_with_pos('use `order by` in ORM queries', order_pos)
+ }
+ has_order = true
+ order_expr = p.expr(0)
+ if p.tok.kind == .name && p.tok.lit == 'desc' {
+ p.check_name() // `desc`
+ has_desc = true
+ }
+ }
+ if p.tok.kind == .name && p.tok.lit == 'limit' {
+ // `limit 1` means that a single object is returned
+ p.check_name() // `limit`
+ if p.tok.kind == .number && p.tok.lit == '1' {
+ query_one = true
+ }
+ has_limit = true
+ limit_expr = p.expr(0)
+ }
+ if p.tok.kind == .name && p.tok.lit == 'offset' {
+ p.check_name() // `offset`
+ has_offset = true
+ offset_expr = p.expr(0)
+ }
+ if !query_one && !is_count {
+ // return an array
+ typ = ast.new_type(p.table.find_or_register_array(table_type))
+ } else if !is_count {
+ // return a single object
+ // TODO optional
+ // typ = table_type.set_flag(.optional)
+ typ = table_type
+ }
+ p.check(.rcbr)
+ return ast.SqlExpr{
+ is_count: is_count
+ typ: typ
+ db_expr: db_expr
+ where_expr: where_expr
+ has_where: has_where
+ has_limit: has_limit
+ limit_expr: limit_expr
+ has_offset: has_offset
+ offset_expr: offset_expr
+ has_order: has_order
+ order_expr: order_expr
+ has_desc: has_desc
+ is_array: !query_one
+ pos: pos.extend(p.prev_tok.position())
+ table_expr: ast.TypeNode{
+ typ: table_type
+ pos: table_pos
+ }
+ }
+}
+
+// insert user into User
+// update User set nr_oders=nr_orders+1 where id == user_id
+fn (mut p Parser) sql_stmt() ast.SqlStmt {
+ mut pos := p.tok.position()
+ p.inside_match = true
+ defer {
+ p.inside_match = false
+ }
+ // `sql db {`
+ p.check_name()
+ db_expr := p.check_expr(0) or {
+ p.error_with_pos('invalid expression: unexpected $p.tok, expecting database',
+ p.tok.position())
+ }
+ // println(typeof(db_expr))
+ p.check(.lcbr)
+
+ mut lines := []ast.SqlStmtLine{}
+
+ for p.tok.kind != .rcbr {
+ lines << p.parse_sql_stmt_line()
+ }
+
+ p.next()
+ pos.last_line = p.prev_tok.line_nr
+ return ast.SqlStmt{
+ pos: pos.extend(p.prev_tok.position())
+ db_expr: db_expr
+ lines: lines
+ }
+}
+
+fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine {
+ mut n := p.check_name() // insert
+ pos := p.tok.position()
+ mut kind := ast.SqlStmtKind.insert
+ if n == 'delete' {
+ kind = .delete
+ } else if n == 'update' {
+ kind = .update
+ } else if n == 'create' {
+ kind = .create
+ table := p.check_name()
+ if table != 'table' {
+ p.error('expected `table` got `$table`')
+ return ast.SqlStmtLine{}
+ }
+ typ := p.parse_type()
+ typ_pos := p.tok.position()
+ return ast.SqlStmtLine{
+ kind: kind
+ pos: pos.extend(p.prev_tok.position())
+ table_expr: ast.TypeNode{
+ typ: typ
+ pos: typ_pos
+ }
+ }
+ } else if n == 'drop' {
+ kind = .drop
+ table := p.check_name()
+ if table != 'table' {
+ p.error('expected `table` got `$table`')
+ return ast.SqlStmtLine{}
+ }
+ typ := p.parse_type()
+ typ_pos := p.tok.position()
+ return ast.SqlStmtLine{
+ kind: kind
+ pos: pos.extend(p.prev_tok.position())
+ table_expr: ast.TypeNode{
+ typ: typ
+ pos: typ_pos
+ }
+ }
+ }
+ mut inserted_var_name := ''
+ mut table_type := ast.Type(0)
+ if kind != .delete {
+ if kind == .update {
+ table_type = p.parse_type()
+ } else if kind == .insert {
+ expr := p.expr(0)
+ if expr is ast.Ident {
+ inserted_var_name = expr.name
+ } else {
+ p.error('can only insert variables')
+ return ast.SqlStmtLine{}
+ }
+ }
+ }
+ n = p.check_name() // into
+ mut updated_columns := []string{}
+ mut update_exprs := []ast.Expr{cap: 5}
+ if kind == .insert && n != 'into' {
+ p.error('expecting `into`')
+ return ast.SqlStmtLine{}
+ } else if kind == .update {
+ if n != 'set' {
+ p.error('expecting `set`')
+ return ast.SqlStmtLine{}
+ }
+ for {
+ column := p.check_name()
+ updated_columns << column
+ p.check(.assign)
+ update_exprs << p.expr(0)
+ if p.tok.kind == .comma {
+ p.check(.comma)
+ } else {
+ break
+ }
+ }
+ } else if kind == .delete && n != 'from' {
+ p.error('expecting `from`')
+ return ast.SqlStmtLine{}
+ }
+
+ mut table_pos := p.tok.position()
+ mut where_expr := ast.empty_expr()
+ if kind == .insert {
+ table_pos = p.tok.position()
+ table_type = p.parse_type()
+ } else if kind == .update {
+ p.check_sql_keyword('where') or { return ast.SqlStmtLine{} }
+ where_expr = p.expr(0)
+ } else if kind == .delete {
+ table_pos = p.tok.position()
+ table_type = p.parse_type()
+ p.check_sql_keyword('where') or { return ast.SqlStmtLine{} }
+ where_expr = p.expr(0)
+ }
+ return ast.SqlStmtLine{
+ table_expr: ast.TypeNode{
+ typ: table_type
+ pos: table_pos
+ }
+ object_var_name: inserted_var_name
+ pos: pos
+ updated_columns: updated_columns
+ update_exprs: update_exprs
+ kind: kind
+ where_expr: where_expr
+ }
+}
+
+fn (mut p Parser) check_sql_keyword(name string) ?bool {
+ if p.check_name() != name {
+ p.error('orm: expecting `$name`')
+ return none
+ }
+ return true
+}