diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index 5be666b8aa..7996bd7174 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -17,7 +17,62 @@ enum SqlExprSide { right } +enum SqlType { + sqlite3 + mysql + psql + unknown +} + 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.writeln('\n\t// sql insert') db_name := g.new_tmp_var() @@ -58,7 +113,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { } else if node.kind == .update { for i, col in node.updated_columns { 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 { g.write(', ') } @@ -68,7 +123,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { g.write(' WHERE ') } if node.kind == .update || node.kind == .delete { - g.expr_to_sql(node.where_expr) + g.expr_to_sql(node.where_expr, typ) } g.writeln('"));') 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);') } -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 /* `nr_users := sql db { ... }` => ``` sql_init_stmt() - sql_bind_int() - sql_bind_string() + sqlite3_bind_int() + sqlite3_bind_string() ... 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(sql_query) 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 { g.write(' ORDER BY ') g.sql_side = .left - g.expr_to_sql(node.order_expr) + g.expr_to_sql(node.order_expr, sql_typ) if node.has_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 { g.write(' LIMIT ') g.sql_side = .right - g.expr_to_sql(node.limit_expr) + g.expr_to_sql(node.limit_expr, sql_typ) } if node.has_offset { g.write(' OFFSET ') g.sql_side = .right - g.expr_to_sql(node.offset_expr) + g.expr_to_sql(node.offset_expr, sql_typ) } g.writeln('"));') // 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);') } -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);') } -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), // 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 { ast.InfixExpr { g.sql_side = .left - g.expr_to_sql(expr.left) + g.expr_to_sql(expr.left, typ) match expr.op { .eq { g.write(' = ') } .gt { g.write(' > ') } @@ -316,22 +387,22 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { else {} } g.sql_side = .right - g.expr_to_sql(expr.right) + g.expr_to_sql(expr.right, typ) } ast.StringLiteral { // g.write("'$it.val'") 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 { g.inc_sql_i() - g.sql_bind_int(expr.val) + g.sql_bind_int(expr.val, typ) } ast.BoolLiteral { // 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 ) 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 { // `name == user_name` => `name == ?1` @@ -342,13 +413,13 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { } else { g.inc_sql_i() info := expr.info as ast.IdentVar - typ := info.typ - if typ == table.string_type { - g.sql_bind_string('${expr.name}.str', '${expr.name}.len') - } else if typ == table.int_type { - g.sql_bind_int(expr.name) + ityp := info.typ + if ityp == table.string_type { + g.sql_bind_string('${expr.name}.str', '${expr.name}.len', typ) + } else if ityp == table.int_type { + g.sql_bind_int(expr.name, typ) } 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') } 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 { 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.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 + } + } +}