mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
V ORM
This commit is contained in:
parent
c67783bcd1
commit
8f8e0dfad7
@ -143,26 +143,6 @@ fn (g mut CGen) set_placeholder(pos int, val string) {
|
||||
// g.genln('')
|
||||
}
|
||||
|
||||
fn (g mut CGen) add_placeholder2() int {
|
||||
if g.is_tmp {
|
||||
println('tmp in addp2')
|
||||
exit(1)
|
||||
}
|
||||
g.lines << ''
|
||||
return g.lines.len - 1
|
||||
}
|
||||
|
||||
fn (g mut CGen) set_placeholder2(pos int, val string) {
|
||||
if g.nogen || g.pass != .main {
|
||||
return
|
||||
}
|
||||
if g.is_tmp {
|
||||
println('tmp in setp2')
|
||||
exit(1)
|
||||
}
|
||||
g.lines[pos] = val
|
||||
}
|
||||
|
||||
fn (g mut CGen) insert_before(val string) {
|
||||
prev := g.lines[g.lines.len - 1]
|
||||
g.lines[g.lines.len - 1] = '$prev \n $val \n'
|
||||
@ -240,7 +220,6 @@ fn (p mut Parser) gen_type_alias(s string) {
|
||||
}
|
||||
|
||||
fn (g mut CGen) add_to_main(s string) {
|
||||
println('add to main')
|
||||
g.fn_main = g.fn_main + s
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ mut:
|
||||
is_alloc bool // Whether current expression resulted in an allocation
|
||||
cur_gen_type string // "App" to replace "T" in current generic function
|
||||
is_vweb bool
|
||||
is_sql bool
|
||||
}
|
||||
|
||||
const (
|
||||
@ -1360,7 +1361,16 @@ fn (p mut Parser) bool_expression() string {
|
||||
got_or = true
|
||||
if got_and { p.error(and_or_error) }
|
||||
}
|
||||
p.gen(' ${p.tok.str()} ')
|
||||
if p.is_sql {
|
||||
if p.tok == .and {
|
||||
p.gen(' and ')
|
||||
}
|
||||
else if p.tok == .logical_or {
|
||||
p.gen(' or ')
|
||||
}
|
||||
} else {
|
||||
p.gen(' ${p.tok.str()} ')
|
||||
}
|
||||
p.check_space(p.tok)
|
||||
p.check_types(p.bterm(), typ)
|
||||
}
|
||||
@ -1377,21 +1387,24 @@ fn (p mut Parser) bterm() string {
|
||||
ph := p.cgen.add_placeholder()
|
||||
mut typ := p.expression()
|
||||
p.expected_type = typ
|
||||
is_str := typ=='string'
|
||||
is_str := typ=='string' && !p.is_sql
|
||||
tok := p.tok
|
||||
// if tok in [ .eq, .gt, .lt, .le, .ge, .ne] {
|
||||
if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne {
|
||||
if tok == .eq || (tok == .assign && p.is_sql) || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne {
|
||||
p.fgen(' ${p.tok.str()} ')
|
||||
if is_str {
|
||||
p.gen(',')
|
||||
}
|
||||
else if p.is_sql && tok == .eq {
|
||||
p.gen('=')
|
||||
}
|
||||
else {
|
||||
p.gen(tok.str())
|
||||
}
|
||||
p.next()
|
||||
p.check_types(p.expression(), typ)
|
||||
typ = 'bool'
|
||||
if is_str {
|
||||
if is_str { //&& !p.is_sql {
|
||||
p.gen(')')
|
||||
switch tok {
|
||||
case Token.eq: p.cgen.set_placeholder(ph, 'string_eq(')
|
||||
@ -1679,7 +1692,6 @@ fn (p mut Parser) var_expr(v Var) string {
|
||||
p.gen(')')
|
||||
typ = T.func.typ
|
||||
}
|
||||
// users[0] before dot so that we can have
|
||||
// users[0].name
|
||||
if p.tok == .lsbr {
|
||||
typ = p.index_expr(typ, fn_ph)
|
||||
@ -1687,6 +1699,15 @@ fn (p mut Parser) var_expr(v Var) string {
|
||||
// a.b.c().d chain
|
||||
// mut dc := 0
|
||||
for p.tok ==.dot {
|
||||
if p.peek() == .key_select {
|
||||
p.next()
|
||||
return p.select_query(fn_ph)
|
||||
}
|
||||
if typ == 'pg__DB' && !p.fileis('pg.v') {
|
||||
p.next()
|
||||
p.insert_query(fn_ph)
|
||||
return 'void'
|
||||
}
|
||||
// println('dot #$dc')
|
||||
typ = p.dot(typ, fn_ph)
|
||||
p.log('typ after dot=$typ')
|
||||
@ -1740,6 +1761,9 @@ fn (p &Parser) fileis(s string) bool {
|
||||
// user.name => `str_typ` is `User`
|
||||
// user.company.name => `str_typ` is `Company`
|
||||
fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
||||
//if p.fileis('orm_test') {
|
||||
//println('ORM dot $str_typ')
|
||||
//}
|
||||
p.check(.dot)
|
||||
typ := p.find_type(str_typ)
|
||||
if typ.name.len == 0 {
|
||||
@ -1949,7 +1973,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
|
||||
// TODO move this from index_expr()
|
||||
// TODO if p.tok in ...
|
||||
// if p.tok in [.assign, .plus_assign, .minus_assign]
|
||||
if p.tok == .assign || p.tok == .plus_assign || p.tok == .minus_assign ||
|
||||
if (p.tok == .assign && !p.is_sql) || p.tok == .plus_assign || p.tok == .minus_assign ||
|
||||
p.tok == .mult_assign || p.tok == .div_assign || p.tok == .xor_assign || p.tok == .mod_assign ||
|
||||
p.tok == .or_assign || p.tok == .and_assign || p.tok == .righ_shift_assign ||
|
||||
p.tok == .left_shift_assign {
|
||||
@ -2285,6 +2309,9 @@ fn (p mut Parser) factor() string {
|
||||
if p.lit == 'json' && p.peek() == .dot {
|
||||
return p.js_decode()
|
||||
}
|
||||
//if p.fileis('orm_test') {
|
||||
//println('ORM name: $p.lit')
|
||||
//}
|
||||
typ = p.name_expr()
|
||||
return typ
|
||||
case Token.key_default:
|
||||
@ -2414,6 +2441,9 @@ fn (p mut Parser) string_expr() {
|
||||
if p.calling_c || (p.pref.translated && p.mod == 'main') {
|
||||
p.gen('"$f"')
|
||||
}
|
||||
else if p.is_sql {
|
||||
p.gen('\'$str\'')
|
||||
}
|
||||
else {
|
||||
p.gen('tos2((byte*)"$f")')
|
||||
}
|
||||
@ -2457,6 +2487,9 @@ fn (p mut Parser) string_expr() {
|
||||
if typ == 'ustring' {
|
||||
args += '.len, ${val}.s.str'
|
||||
}
|
||||
if typ == 'bool' {
|
||||
//args += '.len, ${val}.str'
|
||||
}
|
||||
// Custom format? ${t.hour:02d}
|
||||
custom := p.tok == .colon
|
||||
if custom {
|
||||
@ -2910,9 +2943,9 @@ fn os_name_to_ifdef(name string) string {
|
||||
|
||||
fn (p mut Parser) if_st(is_expr bool, elif_depth int) string {
|
||||
if is_expr {
|
||||
if p.fileis('if_expr') {
|
||||
println('IF EXPR')
|
||||
}
|
||||
//if p.fileis('if_expr') {
|
||||
//println('IF EXPR')
|
||||
//}
|
||||
p.inside_if_expr = true
|
||||
p.gen('(')
|
||||
}
|
||||
|
152
compiler/query.v
Normal file
152
compiler/query.v
Normal file
@ -0,0 +1,152 @@
|
||||
module main
|
||||
|
||||
import strings
|
||||
|
||||
// `db.select from User where id == 1 && nr_bookings > 0`
|
||||
fn (p mut Parser) select_query(fn_ph int) string {
|
||||
mut q := 'select '
|
||||
p.check(.key_select)
|
||||
n := p.check_name()
|
||||
if n == 'count' {
|
||||
q += 'count(*) from '
|
||||
p.check_name()
|
||||
}
|
||||
table_name := p.check_name()
|
||||
// Register this type's fields as variables so they can be used in where expressions
|
||||
typ := p.table.find_type(table_name)
|
||||
if typ.name == '' {
|
||||
p.error('unknown type `$table_name`')
|
||||
}
|
||||
// 'select id, name, age from...'
|
||||
if n == 'from' {
|
||||
for i, field in typ.fields {
|
||||
q += field.name
|
||||
if i < typ.fields.len - 1 {
|
||||
q += ', '
|
||||
}
|
||||
}
|
||||
q += ' from '
|
||||
}
|
||||
for field in typ.fields {
|
||||
//println('registering sql field var $field.name')
|
||||
p.cur_fn.register_var({ field | is_used:true})
|
||||
}
|
||||
q += table_name
|
||||
// `where` statement
|
||||
if p.tok == .name && p.lit == 'where' {
|
||||
p.next()
|
||||
p.cgen.start_tmp()
|
||||
p.is_sql = true
|
||||
p.bool_expression()
|
||||
p.is_sql = false
|
||||
q += ' where ' + p.cgen.end_tmp()
|
||||
}
|
||||
// limit?
|
||||
mut query_one := false
|
||||
if p.tok == .name && p.lit == 'limit' {
|
||||
p.next()
|
||||
p.cgen.start_tmp()
|
||||
p.is_sql = true
|
||||
p.bool_expression()
|
||||
p.is_sql = false
|
||||
limit := p.cgen.end_tmp()
|
||||
q += ' limit ' + limit
|
||||
// `limit 1` means we are getting `User`, not `[]User`
|
||||
if limit.trim_space() == '1' {
|
||||
query_one = true
|
||||
}
|
||||
}
|
||||
//println('sql query="$q"')
|
||||
if n == 'count' {
|
||||
p.cgen.set_placeholder(fn_ph, 'pg__DB_q_int(')
|
||||
p.gen(', tos2("$q"))')
|
||||
} else {
|
||||
// Build an object, assign each field.
|
||||
tmp := p.get_tmp()
|
||||
mut obj_gen := strings.new_builder(100)
|
||||
for i, field in typ.fields {
|
||||
mut cast := ''
|
||||
if field.typ == 'int' {
|
||||
cast = 'string_int'
|
||||
}
|
||||
obj_gen.writeln('$tmp . $field.name = $cast( *(string*)array__get(row.vals, $i) );')
|
||||
}
|
||||
// One object
|
||||
if query_one {
|
||||
p.cgen.insert_before('
|
||||
|
||||
pg__Row row = pg__DB_exec_one(db, tos2("$q"));
|
||||
$table_name $tmp;
|
||||
${obj_gen.str()}
|
||||
|
||||
')
|
||||
p.cgen.resetln(tmp)
|
||||
}
|
||||
// Array
|
||||
else {
|
||||
p.cgen.insert_before('
|
||||
|
||||
array_pg__Row rows = pg__DB_exec(db, tos2("$q"));
|
||||
printf("ROWS LEN=%d\\n", rows.len);
|
||||
// TODO preallocate
|
||||
array arr_$tmp = new_array(0, 0, sizeof($table_name));
|
||||
for (int i = 0; i < rows.len; i++) {
|
||||
pg__Row row = *(pg__Row*)array__get(rows, i);
|
||||
$table_name $tmp;
|
||||
${obj_gen.str()}
|
||||
_PUSH(&arr_$tmp, $tmp, ${tmp}2, $table_name);
|
||||
}
|
||||
')
|
||||
p.cgen.resetln('arr_$tmp')
|
||||
}
|
||||
|
||||
}
|
||||
if n == 'count' {
|
||||
return 'int'
|
||||
} else if query_one {
|
||||
return table_name
|
||||
} else {
|
||||
p.register_array('array_$table_name')
|
||||
return 'array_$table_name'
|
||||
}
|
||||
}
|
||||
|
||||
// `db.insert(user)`
|
||||
fn (p mut Parser) insert_query(fn_ph int) {
|
||||
p.check_name()
|
||||
p.check(.lpar)
|
||||
var_name := p.check_name()
|
||||
p.check(.rpar)
|
||||
var := p.cur_fn.find_var(var_name)
|
||||
typ := p.table.find_type(var.typ)
|
||||
table_name := var.typ
|
||||
mut fields := '' // 'name, city, country'
|
||||
mut params := '' // params[0] = 'bob'; params[1] = 'Vienna';
|
||||
mut vals := '' // $1, $2, $3...
|
||||
mut nr_vals := 0
|
||||
for i, field in typ.fields {
|
||||
if field.name == 'id' {
|
||||
continue
|
||||
}
|
||||
fields += field.name
|
||||
vals += '$' + i.str()
|
||||
nr_vals++
|
||||
params += 'params[${i-1}] = '
|
||||
if field.typ == 'string' {
|
||||
params += '$var_name . $field.name .str;\n'
|
||||
} else if field.typ == 'int' {
|
||||
params += 'int_str($var_name . $field.name).str;\n'
|
||||
} else {
|
||||
p.error('V ORM: unsupported type `$field.typ`')
|
||||
}
|
||||
if i < typ.fields.len - 1 {
|
||||
fields += ', '
|
||||
vals += ', '
|
||||
}
|
||||
}
|
||||
p.cgen.insert_before('char* params[$nr_vals];' + params)
|
||||
p.cgen.set_placeholder(fn_ph, 'PQexecParams( ')
|
||||
p.genln('.conn, "insert into $table_name ($fields) values ($vals)", $nr_vals,
|
||||
0, params, 0, 0, 0)')
|
||||
}
|
||||
|
@ -761,8 +761,9 @@ fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
||||
}
|
||||
switch typ {
|
||||
case 'string': return '%.*s'
|
||||
//case 'bool': return '%.*s'
|
||||
case 'ustring': return '%.*s'
|
||||
case 'byte', 'int', 'char', 'byte', 'bool', 'u32', 'i32', 'i16', 'u16', 'i8', 'u8': return '%d'
|
||||
case 'byte', 'bool', 'int', 'char', 'byte', 'u32', 'i32', 'i16', 'u16', 'i8', 'u8': return '%d'
|
||||
case 'f64', 'f32': return '%f'
|
||||
case 'i64', 'u64': return '%lld'
|
||||
case 'byte*', 'byteptr': return '%s'
|
||||
|
@ -97,6 +97,7 @@ enum Token {
|
||||
key_module
|
||||
key_mut
|
||||
key_return
|
||||
key_select
|
||||
key_sizeof
|
||||
key_struct
|
||||
key_switch
|
||||
@ -221,6 +222,7 @@ fn build_token_str() []string {
|
||||
s[Token.key_as] = 'as'
|
||||
s[Token.key_defer] = 'defer'
|
||||
s[Token.key_match] = 'match'
|
||||
s[Token.key_select] = 'select'
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -535,13 +535,12 @@ pub fn (ar []int) contains(val int) bool {
|
||||
|
||||
/*
|
||||
pub fn (a []string) to_c() voidptr {
|
||||
char ** res = malloc(sizeof(char*) * a.len);
|
||||
mut res := malloc(sizeof(byteptr) * a.len)
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
# res[i] = val.str;
|
||||
res[i] = val.str
|
||||
}
|
||||
return res;
|
||||
return 0
|
||||
return res
|
||||
}
|
||||
*/
|
||||
|
||||
@ -557,7 +556,6 @@ pub fn (s string) trim_space() string {
|
||||
if s == '' {
|
||||
return ''
|
||||
}
|
||||
// println('TRIM SPACE "$s"')
|
||||
mut i := 0
|
||||
for i < s.len && is_space(s[i]) {
|
||||
i++
|
||||
|
@ -1,16 +1,46 @@
|
||||
//import pg
|
||||
import pg
|
||||
|
||||
struct Mod {
|
||||
struct Modules {
|
||||
id int
|
||||
user_id int
|
||||
name string
|
||||
url string
|
||||
nr_downloads int
|
||||
//nr_downloads int
|
||||
}
|
||||
|
||||
fn test_orm() {
|
||||
/*
|
||||
db := pg.connect('vpm', 'alex')
|
||||
nr_modules := select count from db.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
|
||||
name == 'Bob' && id == 1
|
||||
println(nr_modules)
|
||||
|
||||
mod := db.select from Modules where id = 1 limit 1
|
||||
println(mod)
|
||||
|
||||
mods := db.select from Modules limit 10
|
||||
for mod in mods {
|
||||
println(mod)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
mod := db.retrieve<Module>(1)
|
||||
|
||||
mod := db.update Module set name = name + '!' where id > 10
|
||||
|
||||
|
||||
nr_modules := db.select count from Modules
|
||||
where id > 1 && name == ''
|
||||
println(nr_modules)
|
||||
|
||||
nr_modules := db.select count from modules
|
||||
nr_modules := db.select from modules
|
||||
nr_modules := db[:modules].select
|
||||
*/
|
||||
/*
|
||||
mod := select from db.modules where id = 1 limit 1
|
||||
println(mod.name)
|
||||
top_mods := select from db.modules where nr_downloads > 1000 order by nr_downloads desc limit 10
|
||||
|
19
vlib/pg/pg.v
19
vlib/pg/pg.v
@ -30,7 +30,8 @@ fn C.PQgetvalue(voidptr, int, int) byteptr
|
||||
fn C.PQstatus(voidptr) int
|
||||
|
||||
pub fn connect(dbname, user string) DB {
|
||||
conninfo := 'host=localhost user=$user dbname=$dbname'
|
||||
//conninfo := 'host=localhost user=$user dbname=$dbname'
|
||||
conninfo := 'host=127.0.0.1 user=$user dbname=$dbname'
|
||||
conn:=C.PQconnectdb(conninfo.str)
|
||||
status := C.PQstatus(conn)
|
||||
if status != CONNECTION_OK {
|
||||
@ -99,6 +100,21 @@ pub fn (db DB) exec(query string) []pg.Row {
|
||||
return res_to_rows(res)
|
||||
}
|
||||
|
||||
pub fn (db DB) exec_one(query string) pg.Row {
|
||||
res := C.PQexec(db.conn, query.str)
|
||||
e := string(C.PQerrorMessage(db.conn))
|
||||
if e != '' {
|
||||
println('pg exec error:')
|
||||
println(e)
|
||||
return Row{}
|
||||
}
|
||||
rows := res_to_rows(res)
|
||||
if rows.len == 0 {
|
||||
return Row{}
|
||||
}
|
||||
return rows[0]
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
pub fn (db DB) exec_param2(query string, param, param2 string) []pg.Row {
|
||||
@ -122,3 +138,4 @@ pub fn (db DB) exec_param(query string, param string) []pg.Row {
|
||||
return res_to_rows(res)
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user