orm: add type detection of db (#8756)

pull/8662/head
Louis Schmieder 2021-02-15 16:14:39 +01:00 committed by GitHub
parent 94acc27ee6
commit 4bdbb0cfa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 123 additions and 24 deletions

View File

@ -17,7 +17,62 @@ enum SqlExprSide {
right right
} }
enum SqlType {
sqlite3
mysql
psql
unknown
}
fn (mut g Gen) sql_stmt(node ast.SqlStmt) { fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
typ := g.parse_db_type(node.db_expr)
match typ {
.sqlite3 {
g.sqlite3_stmt(node, typ)
}
else {
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
}
}
}
fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
typ := g.parse_db_type(node.db_expr)
match typ {
.sqlite3 {
g.sqlite3_select_expr(node, sub, line, typ)
}
else {
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
}
}
}
fn (mut g Gen) sql_bind_int(val string, typ SqlType) {
match typ {
.sqlite3 {
g.sqlite3_bind_int(val)
}
else {
// add error
}
}
}
fn (mut g Gen) sql_bind_string(val string, len string, typ SqlType) {
match typ {
.sqlite3 {
g.sqlite3_bind_string(val, len)
}
else {
// add error
}
}
}
// sqlite3
fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) {
g.sql_i = 0 g.sql_i = 0
g.writeln('\n\t// sql insert') g.writeln('\n\t// sql insert')
db_name := g.new_tmp_var() db_name := g.new_tmp_var()
@ -58,7 +113,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
} else if node.kind == .update { } else if node.kind == .update {
for i, col in node.updated_columns { for i, col in node.updated_columns {
g.write(' $col = ') g.write(' $col = ')
g.expr_to_sql(node.update_exprs[i]) g.expr_to_sql(node.update_exprs[i], typ)
if i < node.updated_columns.len - 1 { if i < node.updated_columns.len - 1 {
g.write(', ') g.write(', ')
} }
@ -68,7 +123,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.write(' WHERE ') g.write(' WHERE ')
} }
if node.kind == .update || node.kind == .delete { if node.kind == .update || node.kind == .delete {
g.expr_to_sql(node.where_expr) g.expr_to_sql(node.where_expr, typ)
} }
g.writeln('"));') g.writeln('"));')
if node.kind == .insert { if node.kind == .insert {
@ -106,14 +161,14 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
g.writeln('\tsqlite3_finalize($g.sql_stmt_name);') g.writeln('\tsqlite3_finalize($g.sql_stmt_name);')
} }
fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_typ SqlType) {
g.sql_i = 0 g.sql_i = 0
/* /*
`nr_users := sql db { ... }` => `nr_users := sql db { ... }` =>
``` ```
sql_init_stmt() sql_init_stmt()
sql_bind_int() sqlite3_bind_int()
sql_bind_string() sqlite3_bind_string()
... ...
int nr_users = get_int(stmt) int nr_users = get_int(stmt)
``` ```
@ -152,12 +207,12 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("') g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("')
g.write(sql_query) g.write(sql_query)
if node.has_where && node.where_expr is ast.InfixExpr { if node.has_where && node.where_expr is ast.InfixExpr {
g.expr_to_sql(node.where_expr) g.expr_to_sql(node.where_expr, sql_typ)
} }
if node.has_order { if node.has_order {
g.write(' ORDER BY ') g.write(' ORDER BY ')
g.sql_side = .left g.sql_side = .left
g.expr_to_sql(node.order_expr) g.expr_to_sql(node.order_expr, sql_typ)
if node.has_desc { if node.has_desc {
g.write(' DESC ') g.write(' DESC ')
} }
@ -167,12 +222,12 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
if node.has_limit { if node.has_limit {
g.write(' LIMIT ') g.write(' LIMIT ')
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(node.limit_expr) g.expr_to_sql(node.limit_expr, sql_typ)
} }
if node.has_offset { if node.has_offset {
g.write(' OFFSET ') g.write(' OFFSET ')
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(node.offset_expr) g.expr_to_sql(node.offset_expr, sql_typ)
} }
g.writeln('"));') g.writeln('"));')
// Dump all sql parameters generated by our custom expr handler // Dump all sql parameters generated by our custom expr handler
@ -283,15 +338,31 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
} }
} }
fn (mut g Gen) sql_bind_int(val string) { fn (mut g Gen) sqlite3_bind_int(val string) {
g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $val);') g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $val);')
} }
fn (mut g Gen) sql_bind_string(val string, len string) { fn (mut g Gen) sqlite3_bind_string(val string, len string) {
g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, $val, $len, 0);') g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, $val, $len, 0);')
} }
fn (mut g Gen) expr_to_sql(expr ast.Expr) { // mysql
fn (mut g Gen) mysql_stmt(node ast.SqlStmt) {
}
fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string) {
}
fn (mut g Gen) mysql_bind_int(val string) {
}
fn (mut g Gen) mysql_bind_string(val string, len string) {
}
// utils
fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
// Custom handling for infix exprs (since we need e.g. `and` instead of `&&` in SQL queries), // 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() // strings. Everything else (like numbers, a.b) is handled by g.expr()
// //
@ -300,7 +371,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) {
match expr { match expr {
ast.InfixExpr { ast.InfixExpr {
g.sql_side = .left g.sql_side = .left
g.expr_to_sql(expr.left) g.expr_to_sql(expr.left, typ)
match expr.op { match expr.op {
.eq { g.write(' = ') } .eq { g.write(' = ') }
.gt { g.write(' > ') } .gt { g.write(' > ') }
@ -316,22 +387,22 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) {
else {} else {}
} }
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(expr.right) g.expr_to_sql(expr.right, typ)
} }
ast.StringLiteral { ast.StringLiteral {
// g.write("'$it.val'") // g.write("'$it.val'")
g.inc_sql_i() g.inc_sql_i()
g.sql_bind_string('"$expr.val"', expr.val.len.str()) g.sql_bind_string('"$expr.val"', expr.val.len.str(), typ)
} }
ast.IntegerLiteral { ast.IntegerLiteral {
g.inc_sql_i() g.inc_sql_i()
g.sql_bind_int(expr.val) g.sql_bind_int(expr.val, typ)
} }
ast.BoolLiteral { ast.BoolLiteral {
// true/false literals were added to Sqlite 3.23 (2018-04-02) // true/false literals were added to Sqlite 3.23 (2018-04-02)
// but lots of apps/distros use older sqlite (e.g. Ubuntu 18.04 LTS ) // but lots of apps/distros use older sqlite (e.g. Ubuntu 18.04 LTS )
g.inc_sql_i() g.inc_sql_i()
g.sql_bind_int(if expr.val { '1' } else { '0' }) g.sql_bind_int(if expr.val { '1' } else { '0' }, typ)
} }
ast.Ident { ast.Ident {
// `name == user_name` => `name == ?1` // `name == user_name` => `name == ?1`
@ -342,13 +413,13 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) {
} else { } else {
g.inc_sql_i() g.inc_sql_i()
info := expr.info as ast.IdentVar info := expr.info as ast.IdentVar
typ := info.typ ityp := info.typ
if typ == table.string_type { if ityp == table.string_type {
g.sql_bind_string('${expr.name}.str', '${expr.name}.len') g.sql_bind_string('${expr.name}.str', '${expr.name}.len', typ)
} else if typ == table.int_type { } else if ityp == table.int_type {
g.sql_bind_int(expr.name) g.sql_bind_int(expr.name, typ)
} else { } else {
verror('bad sql type=$typ ident_name=$expr.name') verror('bad sql type=$ityp ident_name=$expr.name')
} }
} }
} }
@ -359,7 +430,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) {
verror('orm selector not ident') verror('orm selector not ident')
} }
ident := expr.expr as ast.Ident ident := expr.expr as ast.Ident
g.sql_bind_int(ident.name + '.' + expr.field_name) g.sql_bind_int(ident.name + '.' + expr.field_name, typ)
} else { } else {
verror('bad sql type=$expr.typ selector expr=$expr.field_name') verror('bad sql type=$expr.typ selector expr=$expr.field_name')
} }
@ -380,3 +451,31 @@ fn (mut g Gen) inc_sql_i() {
g.sql_i++ g.sql_i++
g.write('?$g.sql_i') g.write('?$g.sql_i')
} }
fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
match expr {
ast.Ident {
if expr.info is ast.IdentVar {
return g.parse_db_from_type_string(g.table.get_type_name(expr.info.typ))
}
}
ast.SelectorExpr {
return g.parse_db_from_type_string(g.table.get_type_name(expr.typ))
}
else {
return .unknown
}
}
return .unknown
}
fn (mut g Gen) parse_db_from_type_string(name string) SqlType {
match name {
'sqlite.DB' {
return .sqlite3
}
else {
return .unknown
}
}
}