Skip to content

Commit

Permalink
orm: insert expressions returning id
Browse files Browse the repository at this point in the history
  • Loading branch information
medvednikov committed Mar 11, 2024
1 parent b2df326 commit 96aa23f
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 8 deletions.
10 changes: 9 additions & 1 deletion vlib/v/ast/ast.v
Expand Up @@ -1960,6 +1960,11 @@ pub enum SqlStmtKind {
drop
}

pub enum SqlExprKind {
insert
select_
}

pub struct SqlStmt {
pub:
pos token.Pos
Expand Down Expand Up @@ -1989,7 +1994,10 @@ pub mut:

pub struct SqlExpr {
pub:
is_count bool
is_count bool
is_insert bool // for insert expressions
inserted_var string

has_where bool
has_order bool
has_limit bool
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/checker/orm.v
Expand Up @@ -190,9 +190,16 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
'offset')
}
c.expr(mut node.db_expr)
if node.is_insert {
node.typ = ast.int_type
}

c.check_orm_or_expr(mut node)

if node.is_insert {
return ast.int_type
}

return node.typ.clear_flag(.result)
}

Expand Down
12 changes: 10 additions & 2 deletions vlib/v/fmt/fmt.v
Expand Up @@ -2985,7 +2985,11 @@ pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) {
f.write('sql ')
f.expr(node.db_expr)
f.writeln(' {')
f.write('\tselect ')
if node.is_insert {
f.write('\tinsert ')
} else {
f.write('\tselect ')
}
sym := f.table.sym(node.table_expr.typ)
mut table_name := sym.name
if !table_name.starts_with('C.') && !table_name.starts_with('JS.') {
Expand All @@ -3001,7 +3005,11 @@ pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) {
}
}
}
f.write('from ${table_name}')
if node.is_insert {
f.write('${node.inserted_var} into ${table_name}')
} else {
f.write('from ${table_name}')
}
if node.has_where {
f.write(' where ')
f.expr(node.where_expr)
Expand Down
6 changes: 5 additions & 1 deletion vlib/v/gen/c/cgen.v
Expand Up @@ -3527,7 +3527,11 @@ fn (mut g Gen) expr(node_ ast.Expr) {
g.size_of(node)
}
ast.SqlExpr {
g.sql_select_expr(node)
if node.is_insert {
g.sql_insert_expr(node)
} else {
g.sql_select_expr(node)
}
}
ast.StringLiteral {
g.string_literal(node)
Expand Down
28 changes: 27 additions & 1 deletion vlib/v/gen/c/orm.v
Expand Up @@ -42,6 +42,30 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
g.write('${left} *(${unwrapped_c_typ}*)${result_var}.data')
}

fn (mut g Gen) sql_insert_expr(node ast.SqlExpr) {
left := g.go_before_last_stmt()
g.writeln('')
connection_var_name := g.new_tmp_var()
g.write_orm_connection_init(connection_var_name, &node.db_expr)
table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
result_var_name := g.new_tmp_var()
g.sql_table_name = g.table.sym(node.table_expr.typ).name

// orm_insert needs an SqlStmtLine, build it from SqlExpr (most nodes are the same)
hack_stmt_line := ast.SqlStmtLine{
object_var: node.inserted_var
fields: node.fields
// sub_structs: node.sub_structs
}
g.write_orm_insert(hack_stmt_line, table_name, connection_var_name, result_var_name,
node.or_expr)

g.write(left)
g.write('db__pg__DB_last_id(')
g.expr(node.db_expr)
g.write(');')
}

// sql_stmt writes C code that calls ORM functions for
// performing various database operations such as creating and dropping tables,
// as well as inserting and updating objects.
Expand Down Expand Up @@ -272,6 +296,7 @@ fn (mut g Gen) write_orm_delete(node &ast.SqlStmtLine, table_name string, connec
// inserting a struct into a table, saving inserted `id` into a passed variable.
fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_var_name string, table_name string, last_ids_arr string, res string, pid string, fkey string, or_expr ast.OrExpr) {
mut subs := []ast.SqlStmtLine{}

mut subs_unwrapped_c_typ := []string{}
mut arrs := []ast.SqlStmtLine{}
mut fkeys := []string{}
Expand All @@ -285,6 +310,7 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v
unwrapped_c_typ := g.typ(field.typ.clear_flag(.option))
subs_unwrapped_c_typ << if field.typ.has_flag(.option) { unwrapped_c_typ } else { '' }
} else if sym.kind == .array {
// Handle foreign keys
if attr := field.attrs.find_first('fkey') {
fkeys << attr.arg
} else {
Expand Down Expand Up @@ -475,7 +501,7 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v
unsafe { fff.free() }
g.write_orm_insert_with_last_ids(arr, connection_var_name, g.get_table_name_by_struct_type(arr.table_expr.typ),
last_ids, res_, id_name, fkeys[i], or_expr)
// Validates sub insertion success otherwise, handled and propagated error.
// Validates sub insertion success otherwise, handled and propagated error.
g.or_block(res_, or_expr, ast.int_type.set_flag(.result))
g.indent--
g.writeln('}')
Expand Down
31 changes: 28 additions & 3 deletions vlib/v/parser/orm.v
Expand Up @@ -5,6 +5,8 @@ module parser

import v.ast

// select from User
// insert user into User returning id
fn (mut p Parser) sql_expr() ast.Expr {
tmp_inside_match := p.inside_match
p.inside_orm = true
Expand All @@ -16,12 +18,32 @@ fn (mut p Parser) sql_expr() ast.Expr {
p.unexpected(prepend_msg: 'invalid expression:', expecting: 'database')
}
p.check(.lcbr)
p.check(.key_select)
is_count := p.check_name() == 'count'
// p.check(.key_select)
is_select := p.tok.kind == .key_select
is_insert := p.tok.lit == 'insert'
if !is_select && !is_insert {
p.error('expected "select" or "insert" in an ORM expression')
}
p.next()
// kind := if is_select { ast.SqlExprKind.select_ } else { ast.SqlExprKind.insert }
mut inserted_var := ''
mut is_count := false
if is_insert {
inserted_var = p.check_name()
into := p.check_name()
if into != 'into' {
p.error('expecting `into`')
}
} else if is_select {
is_count = p.check_name() == 'count'
}
mut typ := ast.void_type

if is_count {
p.check_name() // from
n := p.check_name() // from
if n != 'from' {
p.error('expecting "from" in a "select count" ORM statement')
}
}

table_pos := p.tok.pos()
Expand Down Expand Up @@ -90,6 +112,7 @@ fn (mut p Parser) sql_expr() ast.Expr {

return ast.SqlExpr{
is_count: is_count
is_insert: is_insert
typ: typ.set_flag(.result)
or_expr: or_expr
db_expr: db_expr
Expand All @@ -104,6 +127,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
has_desc: has_desc
is_array: if is_count { false } else { true }
is_generated: false
inserted_var: inserted_var
pos: pos.extend(p.prev_tok.pos())
table_expr: ast.TypeNode{
typ: table_type
Expand All @@ -114,6 +138,7 @@ fn (mut p Parser) sql_expr() ast.Expr {

// insert user into User
// update User set nr_oders=nr_orders+1 where id == user_id
// delete
fn (mut p Parser) sql_stmt() ast.SqlStmt {
mut pos := p.tok.pos()
p.inside_orm = true
Expand Down

0 comments on commit 96aa23f

Please sign in to comment.