v orm: select
parent
23993d2264
commit
ed58192e4c
|
@ -1,6 +1,6 @@
|
||||||
import os
|
//import os
|
||||||
import pg
|
//import pg
|
||||||
import term
|
//import term
|
||||||
import sqlite
|
import sqlite
|
||||||
|
|
||||||
struct Modules {
|
struct Modules {
|
||||||
|
@ -11,30 +11,48 @@ struct Modules {
|
||||||
//nr_downloads int
|
//nr_downloads int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct User {
|
||||||
|
id int
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
fn test_orm_sqlite() {
|
fn test_orm_sqlite() {
|
||||||
db := sqlite.connect(':memory:') or { panic(err) }
|
db := sqlite.connect(':memory:') or { panic(err) }
|
||||||
/*
|
db.exec("drop table if exists User")
|
||||||
db.exec("drop table if exists users")
|
db.exec("create table User (id integer primary key, name text default '');")
|
||||||
db.exec("create table users (id integer primary key, name text default '');")
|
|
||||||
|
|
||||||
db.exec("insert into users (name) values ('Sam')")
|
name := 'sam'
|
||||||
db.exec("insert into users (name) values ('Peter')")
|
|
||||||
db.exec("insert into users (name) values ('Kate')")
|
db.exec("insert into User (name) values ('Sam')")
|
||||||
nr_users := sql db {
|
db.exec("insert into User (name) values ('Peter')")
|
||||||
//select count from modules
|
db.exec("insert into User (name) values ('Kate')")
|
||||||
|
nr_all_users := sql db {
|
||||||
|
select count from User
|
||||||
}
|
}
|
||||||
assert nr_users == 3
|
assert nr_all_users == 3
|
||||||
println('nr_users=')
|
println('nr_all_users=$nr_all_users')
|
||||||
println(nr_users)
|
//
|
||||||
//nr_modules := db.select count from modules
|
nr_users1 := sql db {
|
||||||
//nr_modules := db.select count from Modules where id == 1
|
select count from User where id == 1
|
||||||
//nr_modules := db.select count from Modules where
|
}
|
||||||
//name == 'Bob' && id == 1
|
assert nr_users1 == 1
|
||||||
*/
|
println('nr_users1=$nr_users1')
|
||||||
|
//
|
||||||
|
nr_peters := sql db {
|
||||||
|
select count from User where id == 2 && name == 'Peter'
|
||||||
|
}
|
||||||
|
assert nr_peters == 1
|
||||||
|
println('nr_peters=$nr_peters')
|
||||||
|
//
|
||||||
|
nr_sams := sql db {
|
||||||
|
select count from User where id == 1 && name == name
|
||||||
|
}
|
||||||
|
println('nr_sams=$nr_sams')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn test_orm_pg() {
|
fn test_orm_pg() {
|
||||||
|
/*
|
||||||
dbname := os.getenv('VDB_NAME')
|
dbname := os.getenv('VDB_NAME')
|
||||||
dbuser := os.getenv('VDB_USER')
|
dbuser := os.getenv('VDB_USER')
|
||||||
if dbname == '' || dbuser == '' {
|
if dbname == '' || dbuser == '' {
|
||||||
|
@ -43,8 +61,7 @@ fn test_orm_pg() {
|
||||||
}
|
}
|
||||||
db := pg.connect(dbname: dbname, user: dbuser) or { panic(err) }
|
db := pg.connect(dbname: dbname, user: dbuser) or { panic(err) }
|
||||||
_ = db
|
_ = db
|
||||||
/*
|
nr_modules := db.select count from modules
|
||||||
//nr_modules := db.select count from modules
|
|
||||||
//nr_modules := db.select count from Modules where id == 1
|
//nr_modules := db.select count from Modules where id == 1
|
||||||
nr_modules := db.select count from Modules where
|
nr_modules := db.select count from Modules where
|
||||||
name == 'Bob' && id == 1
|
name == 'Bob' && id == 1
|
||||||
|
@ -61,6 +78,7 @@ fn test_orm_pg() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
mod := db.retrieve<Module>(1)
|
mod := db.retrieve<Module>(1)
|
||||||
|
mod := db.select from Module where id = 1
|
||||||
|
|
||||||
mod := db.update Module set name = name + '!' where id > 10
|
mod := db.update Module set name = name + '!' where id > 10
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,21 @@ pub fn connect(path string) ?DB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only for V ORM
|
||||||
|
fn (db DB) init_stmt(query string) &C.sqlite3_stmt {
|
||||||
|
stmt := &C.sqlite3_stmt(0)
|
||||||
|
C.sqlite3_prepare_v2(db.conn, query.str, -1, &stmt, 0)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for V ORM
|
||||||
|
fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
|
||||||
|
C.sqlite3_step(stmt)
|
||||||
|
res := C.sqlite3_column_int(stmt, 0)
|
||||||
|
C.sqlite3_finalize(stmt)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a single cell with value int.
|
// Returns a single cell with value int.
|
||||||
pub fn (db DB) q_int(query string) int {
|
pub fn (db DB) q_int(query string) int {
|
||||||
stmt := &C.sqlite3_stmt(0)
|
stmt := &C.sqlite3_stmt(0)
|
||||||
|
|
|
@ -49,6 +49,24 @@ pub fn (mut b Builder) go_back(n int) {
|
||||||
b.len -= n
|
b.len -= n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) cut_last(n int) string {
|
||||||
|
buf := b.buf[b.len-n..]
|
||||||
|
s := string(buf.clone())
|
||||||
|
b.buf.trim(b.buf.len-n)
|
||||||
|
b.len -= n
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn (mut b Builder) cut_to(pos int) string {
|
||||||
|
buf := b.buf[pos..]
|
||||||
|
s := string(buf.clone())
|
||||||
|
b.buf.trim(pos)
|
||||||
|
b.len = pos
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn (mut b Builder) go_back_to(pos int) {
|
pub fn (mut b Builder) go_back_to(pos int) {
|
||||||
b.buf.trim(pos)
|
b.buf.trim(pos)
|
||||||
b.len = pos
|
b.len = pos
|
||||||
|
|
|
@ -8,11 +8,25 @@ fn test_sb() {
|
||||||
assert sb.len == 8
|
assert sb.len == 8
|
||||||
assert sb.str() == 'hi!hello'
|
assert sb.str() == 'hi!hello'
|
||||||
assert sb.len == 0
|
assert sb.len == 0
|
||||||
|
///
|
||||||
sb = strings.new_builder(10)
|
sb = strings.new_builder(10)
|
||||||
sb.write('a')
|
sb.write('a')
|
||||||
sb.write('b')
|
sb.write('b')
|
||||||
assert sb.len == 2
|
assert sb.len == 2
|
||||||
assert sb.str() == 'ab'
|
assert sb.str() == 'ab'
|
||||||
|
///
|
||||||
|
sb = strings.new_builder(10)
|
||||||
|
sb.write('123456')
|
||||||
|
assert sb.cut_last(2) == '56'
|
||||||
|
assert sb.str() == '1234'
|
||||||
|
///
|
||||||
|
/*
|
||||||
|
sb = strings.new_builder(10)
|
||||||
|
sb.write('123456')
|
||||||
|
x := sb.cut_to(2)
|
||||||
|
assert x == '456'
|
||||||
|
assert sb.str() == '123'
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -61,6 +61,10 @@ pub fn header(text, divider string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supports_escape_sequences(fd int) bool {
|
fn supports_escape_sequences(fd int) bool {
|
||||||
|
//println('TERM=' + os.getenv('TERM'))
|
||||||
|
if os.getenv('TERM') == 'dumb' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
vcolors_override := os.getenv('VCOLORS')
|
vcolors_override := os.getenv('VCOLORS')
|
||||||
if vcolors_override == 'always' {
|
if vcolors_override == 'always' {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -801,7 +801,13 @@ pub:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SqlExpr {
|
pub struct SqlExpr {
|
||||||
|
pub:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
|
is_count bool
|
||||||
|
db_var_name string // `db` in `sql db {`
|
||||||
|
table_name string
|
||||||
|
where_expr Expr
|
||||||
|
has_where bool
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
|
|
|
@ -556,7 +556,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
|
||||||
c.error('$infix_expr.op.str(): type `${typ_sym.name}` does not exist', type_expr.pos)
|
c.error('$infix_expr.op.str(): type `${typ_sym.name}` does not exist', type_expr.pos)
|
||||||
}
|
}
|
||||||
if left.kind != .interface_ && left.kind != .sum_type {
|
if left.kind != .interface_ && left.kind != .sum_type {
|
||||||
c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types', type_expr.pos)
|
c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types',
|
||||||
|
type_expr.pos)
|
||||||
}
|
}
|
||||||
return table.bool_type
|
return table.bool_type
|
||||||
}
|
}
|
||||||
|
@ -681,9 +682,12 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
if it.decl.args.len > 1 {
|
if it.decl.args.len > 1 {
|
||||||
c.error('function needs exactly 1 argument', call_expr.pos)
|
c.error('function needs exactly 1 argument', call_expr.pos)
|
||||||
} else if is_map && (it.decl.return_type != elem_typ || it.decl.args[0].typ != elem_typ) {
|
} else if is_map && (it.decl.return_type != elem_typ || it.decl.args[0].typ !=
|
||||||
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos)
|
elem_typ) {
|
||||||
} else if !is_map && (it.decl.return_type != table.bool_type || it.decl.args[0].typ != elem_typ) {
|
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`',
|
||||||
|
call_expr.pos)
|
||||||
|
} else if !is_map && (it.decl.return_type != table.bool_type || it.decl.args[0].typ !=
|
||||||
|
elem_typ) {
|
||||||
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos)
|
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -696,9 +700,12 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
||||||
if func.args.len > 1 {
|
if func.args.len > 1 {
|
||||||
c.error('function needs exactly 1 argument', call_expr.pos)
|
c.error('function needs exactly 1 argument', call_expr.pos)
|
||||||
} else if is_map && (func.return_type != elem_typ || func.args[0].typ != elem_typ) {
|
} else if is_map && (func.return_type != elem_typ || func.args[0].typ != elem_typ) {
|
||||||
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos)
|
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`',
|
||||||
} else if !is_map && (func.return_type != table.bool_type || func.args[0].typ != elem_typ) {
|
call_expr.pos)
|
||||||
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos)
|
} else if !is_map && (func.return_type != table.bool_type || func.args[0].typ !=
|
||||||
|
elem_typ) {
|
||||||
|
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
|
||||||
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1948,15 +1955,29 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||||
ast.None {
|
ast.None {
|
||||||
return table.none_type
|
return table.none_type
|
||||||
}
|
}
|
||||||
|
ast.OrExpr {
|
||||||
|
// never happens
|
||||||
|
return table.void_type
|
||||||
|
}
|
||||||
ast.ParExpr {
|
ast.ParExpr {
|
||||||
return c.expr(it.expr)
|
return c.expr(it.expr)
|
||||||
}
|
}
|
||||||
|
ast.RangeExpr {
|
||||||
|
// never happens
|
||||||
|
return table.void_type
|
||||||
|
}
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
return c.selector_expr(mut it)
|
return c.selector_expr(mut it)
|
||||||
}
|
}
|
||||||
ast.SizeOf {
|
ast.SizeOf {
|
||||||
return table.u32_type
|
return table.u32_type
|
||||||
}
|
}
|
||||||
|
ast.SqlExpr {
|
||||||
|
if it.has_where {
|
||||||
|
c.expr(it.where_expr)
|
||||||
|
}
|
||||||
|
return it.typ
|
||||||
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
if it.language == .c {
|
if it.language == .c {
|
||||||
return table.byteptr_type
|
return table.byteptr_type
|
||||||
|
@ -1986,12 +2007,6 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||||
}
|
}
|
||||||
return table.bool_type
|
return table.bool_type
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
tnode := typeof(node)
|
|
||||||
if tnode != 'unknown v.ast.Expr' {
|
|
||||||
println('checker.expr(): unhandled node with typeof(`${tnode}`)')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ mut:
|
||||||
options strings.Builder // `Option_xxxx` types
|
options strings.Builder // `Option_xxxx` types
|
||||||
json_forward_decls strings.Builder // json type forward decls
|
json_forward_decls strings.Builder // json type forward decls
|
||||||
enum_typedefs strings.Builder // enum types
|
enum_typedefs strings.Builder // enum types
|
||||||
|
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
||||||
file ast.File
|
file ast.File
|
||||||
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
||||||
last_fn_c_name string
|
last_fn_c_name string
|
||||||
|
@ -76,6 +77,7 @@ mut:
|
||||||
is_assign_rhs bool // inside right part of assign after `=` (val expr)
|
is_assign_rhs bool // inside right part of assign after `=` (val expr)
|
||||||
is_array_set bool
|
is_array_set bool
|
||||||
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
|
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
|
||||||
|
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
|
||||||
optionals []string // to avoid duplicates TODO perf, use map
|
optionals []string // to avoid duplicates TODO perf, use map
|
||||||
inside_ternary int // ?: comma separated statements on a single line
|
inside_ternary int // ?: comma separated statements on a single line
|
||||||
ternary_names map[string]string
|
ternary_names map[string]string
|
||||||
|
@ -102,6 +104,8 @@ mut:
|
||||||
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
||||||
cur_fn &ast.FnDecl
|
cur_fn &ast.FnDecl
|
||||||
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
|
sql_i int
|
||||||
|
sql_stmt_name string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -131,6 +135,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
options: strings.new_builder(100)
|
options: strings.new_builder(100)
|
||||||
json_forward_decls: strings.new_builder(100)
|
json_forward_decls: strings.new_builder(100)
|
||||||
enum_typedefs: strings.new_builder(100)
|
enum_typedefs: strings.new_builder(100)
|
||||||
|
sql_buf: strings.new_builder(100)
|
||||||
table: table
|
table: table
|
||||||
pref: pref
|
pref: pref
|
||||||
fn_decl: 0
|
fn_decl: 0
|
||||||
|
@ -1566,7 +1571,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
g.write('sizeof($styp)')
|
g.write('sizeof($styp)')
|
||||||
}
|
}
|
||||||
ast.SqlExpr {
|
ast.SqlExpr {
|
||||||
g.write('// sql expression')
|
g.sql_expr(it)
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
if it.is_raw {
|
if it.is_raw {
|
||||||
|
@ -1630,10 +1635,6 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
g.expr(it.expr)
|
g.expr(it.expr)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
//else {
|
|
||||||
// #printf("node=%d\n", node.typ);
|
|
||||||
//println(term.red('cgen.expr(): bad node ' + typeof(node)))
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3173,6 +3174,15 @@ fn (mut g Gen) insert_before_stmt(s string) {
|
||||||
g.write(cur_line)
|
g.write(cur_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) write_expr_to_string(expr ast.Expr) string {
|
||||||
|
pos := g.out.buf.len
|
||||||
|
g.expr(expr)
|
||||||
|
return g.out.cut_last(g.out.buf.len - pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) start_tmp() {
|
||||||
|
}
|
||||||
|
|
||||||
// If user is accessing the return value eg. in assigment, pass the variable name.
|
// If user is accessing the return value eg. in assigment, pass the variable name.
|
||||||
// If the user is not using the optional return value. We need to pass a temp var
|
// If the user is not using the optional return value. We need to pass a temp var
|
||||||
// to access its fields (`.ok`, `.error` etc)
|
// to access its fields (`.ok`, `.error` etc)
|
||||||
|
@ -3263,7 +3273,6 @@ fn (mut g Gen) in_optimization(left ast.Expr, right ast.ArrayInit) {
|
||||||
ptr_typ := g.gen_array_equality_fn(right.elem_type)
|
ptr_typ := g.gen_array_equality_fn(right.elem_type)
|
||||||
g.write('${ptr_typ}_arr_eq(')
|
g.write('${ptr_typ}_arr_eq(')
|
||||||
}
|
}
|
||||||
|
|
||||||
g.expr(left)
|
g.expr(left)
|
||||||
if is_str || is_array {
|
if is_str || is_array {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
|
@ -4208,7 +4217,6 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
|
||||||
if it.exprs.len == 0 {
|
if it.exprs.len == 0 {
|
||||||
elem_sym := g.table.get_type_symbol(it.elem_type)
|
elem_sym := g.table.get_type_symbol(it.elem_type)
|
||||||
is_default_array := elem_sym.kind == .array && it.has_default
|
is_default_array := elem_sym.kind == .array && it.has_default
|
||||||
|
|
||||||
if is_default_array {
|
if is_default_array {
|
||||||
g.write('__new_array_with_array_default(')
|
g.write('__new_array_with_array_default(')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright (c) 2019-2020 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 gen
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import strings
|
||||||
|
|
||||||
|
// pg,mysql etc
|
||||||
|
const (
|
||||||
|
dbtype = 'sqlite'
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (mut g Gen) sql_expr(node ast.SqlExpr) {
|
||||||
|
g.sql_i = 0
|
||||||
|
/*
|
||||||
|
`nr_users := sql db { ... }` =>
|
||||||
|
```
|
||||||
|
sql_init_stmt()
|
||||||
|
sql_bind_int()
|
||||||
|
sql_bind_string()
|
||||||
|
...
|
||||||
|
int nr_users = get_int(stmt)
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
cur_line := g.go_before_stmt(0)
|
||||||
|
mut q := 'select '
|
||||||
|
if node.is_count {
|
||||||
|
// select count(*) from User
|
||||||
|
q += 'count(*) from $node.table_name'
|
||||||
|
}
|
||||||
|
if node.has_where {
|
||||||
|
q += ' where '
|
||||||
|
}
|
||||||
|
// g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q')
|
||||||
|
g.sql_stmt_name = g.new_tmp_var()
|
||||||
|
db_name := g.new_tmp_var()
|
||||||
|
g.writeln('\n\t// sql')
|
||||||
|
g.write('${dbtype}__DB $db_name = *(${dbtype}__DB*)${node.db_var_name}.data;')
|
||||||
|
g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q')
|
||||||
|
if node.has_where && node.where_expr is ast.InfixExpr {
|
||||||
|
g.expr_to_sql(node.where_expr)
|
||||||
|
}
|
||||||
|
g.writeln('"));')
|
||||||
|
// Dump all sql parameters generated by our custom expr handler
|
||||||
|
binds := g.sql_buf.str()
|
||||||
|
g.sql_buf = strings.new_builder(100)
|
||||||
|
g.writeln(binds)
|
||||||
|
g.writeln('puts(sqlite3_errmsg(${db_name}.conn));')
|
||||||
|
g.writeln('$cur_line ${dbtype}__get_int_from_stmt($g.sql_stmt_name);')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) expr_to_sql(expr ast.Expr) {
|
||||||
|
// Custom handling for infix exprs (since we need e.g. `and` instead of `&&` in SQL queries),
|
||||||
|
// strings. Everything else (like numbers, a.b) is handled by g.expr()
|
||||||
|
//
|
||||||
|
// TODO `where id = some_column + 1` needs literal generation of `some_column` as a string,
|
||||||
|
// not a V variable. Need to distinguish column names from V variables.
|
||||||
|
match expr {
|
||||||
|
ast.InfixExpr {
|
||||||
|
g.expr_to_sql(it.left)
|
||||||
|
match it.op {
|
||||||
|
.eq { g.write(' = ') }
|
||||||
|
.and { g.write(' and ') }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
g.expr_to_sql(it.right)
|
||||||
|
}
|
||||||
|
ast.StringLiteral {
|
||||||
|
// g.write("'$it.val'")
|
||||||
|
g.inc_sql_i()
|
||||||
|
g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, "$it.val", $it.val.len, 0);')
|
||||||
|
}
|
||||||
|
ast.IntegerLiteral {
|
||||||
|
g.inc_sql_i()
|
||||||
|
g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $it.val);')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g.expr(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ast.Ident {
|
||||||
|
g.write('$it.name')
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) inc_sql_i() {
|
||||||
|
g.sql_i++
|
||||||
|
g.write('?$g.sql_i')
|
||||||
|
}
|
|
@ -4,7 +4,76 @@
|
||||||
module parser
|
module parser
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
|
||||||
fn (mut p Parser) sql_expr() ast.SqlExpr {
|
fn (mut p Parser) sql_expr() ast.SqlExpr {
|
||||||
return ast.SqlExpr{}
|
// `sql db {`
|
||||||
|
p.check_name()
|
||||||
|
db_var_name := p.check_name()
|
||||||
|
p.check(.lcbr)
|
||||||
|
//
|
||||||
|
p.check(.key_select)
|
||||||
|
n := p.check_name()
|
||||||
|
is_count := n == 'count'
|
||||||
|
mut typ := table.void_type
|
||||||
|
if is_count {
|
||||||
|
p.check_name() // from
|
||||||
|
typ = table.int_type
|
||||||
|
}
|
||||||
|
table_type := p.parse_type() // `User`
|
||||||
|
sym := p.table.get_type_symbol(table_type)
|
||||||
|
table_name := sym.name
|
||||||
|
mut where_expr := ast.Expr{}
|
||||||
|
has_where := p.tok.kind == .name && p.tok.lit == 'where'
|
||||||
|
if has_where {
|
||||||
|
p.next()
|
||||||
|
where_expr = p.expr(0)
|
||||||
|
}
|
||||||
|
p.check(.rcbr)
|
||||||
|
// /////////
|
||||||
|
// Register this type's fields as variables so they can be used in `where`
|
||||||
|
// expressions
|
||||||
|
// fields := typ.fields.filter(typ == 'string' || typ == 'int')
|
||||||
|
// fields := typ.fields
|
||||||
|
// get only string and int fields
|
||||||
|
// mut fields := []Var
|
||||||
|
info := sym.info as table.Struct
|
||||||
|
fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type])
|
||||||
|
/*
|
||||||
|
for i, field in info.fields {
|
||||||
|
if !(field.typ in ['string', 'int', 'bool']) {
|
||||||
|
println('orm: skipping $field.name')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.attr.contains('skip') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields << field
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if fields.len == 0 {
|
||||||
|
p.error('V orm: select: empty fields in `$table_name`')
|
||||||
|
}
|
||||||
|
if fields[0].name != 'id' {
|
||||||
|
p.error('V orm: `id int` must be the first field in `$table_name`')
|
||||||
|
}
|
||||||
|
for field in fields {
|
||||||
|
// println('registering sql field var $field.name')
|
||||||
|
p.scope.register(field.name, ast.Var{
|
||||||
|
name: field.name
|
||||||
|
typ: field.typ
|
||||||
|
is_mut: true
|
||||||
|
is_used: true
|
||||||
|
is_changed: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// ////////////
|
||||||
|
return ast.SqlExpr{
|
||||||
|
is_count: is_count
|
||||||
|
typ: typ
|
||||||
|
db_var_name: db_var_name
|
||||||
|
table_name: table_name
|
||||||
|
where_expr: where_expr
|
||||||
|
has_where: has_where
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue