diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index e29d4435e1..408f35da40 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -10,7 +10,7 @@ struct Module { } struct User { - id int + id int [primary] age int name string is_customer bool @@ -24,7 +24,9 @@ struct Foo { fn test_orm_sqlite() { db := sqlite.connect(':memory:') or { panic(err) } db.exec('drop table if exists User') - db.exec("create table User (id integer primary key, age int default 0, name text default '', is_customer int default 0);") + sql db { + create table User + } name := 'Peter' db.exec("insert into User (name, age) values ('Sam', 29)") db.exec("insert into User (name, age) values ('Peter', 31)") diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index aa02f3e897..f5501da715 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1410,6 +1410,7 @@ pub enum SqlStmtKind { insert update delete + create } pub struct SqlStmt { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 6a2309af32..58df0f9754 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1232,6 +1232,9 @@ pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) { f.expr(node.where_expr) f.writeln('') } + .create { + f.writeln('create table $table_name') + } } f.writeln('}') } diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index 09d65bcc90..4ec32864d0 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -24,6 +24,10 @@ enum SqlType { } fn (mut g Gen) sql_stmt(node ast.SqlStmt) { + if node.kind == .create { + g.sql_create_table(node) + return + } typ := g.parse_db_type(node.db_expr) match typ { .sqlite3 { @@ -35,6 +39,18 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { } } +fn (mut g Gen) sql_create_table(node ast.SqlStmt) { + typ := g.parse_db_type(node.db_expr) + match typ { + .sqlite3 { + g.sqlite3_create_table(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 { @@ -69,6 +85,18 @@ fn (mut g Gen) sql_bind_string(val string, len string, typ SqlType) { } } +fn (mut g Gen) sql_type_from_v(typ SqlType, v_typ ast.Type) string { + match typ { + .sqlite3 { + return g.sqlite3_type_from_v(typ, v_typ) + } + else { + // add error + } + } + return '' +} + // sqlite3 fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) { @@ -337,6 +365,70 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_ } } +fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) { + typ_sym := g.table.get_type_symbol(node.table_expr.typ) + if typ_sym.info !is ast.Struct { + verror('Type `$typ_sym.name` has to be a struct') + } + g.writeln('// sqlite3 table creator ($typ_sym.name)') + struct_data := typ_sym.info as ast.Struct + table_name := typ_sym.name.split('.').last() + mut create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` (' + + mut fields := []string{} + + outer: for field in struct_data.fields { + mut is_primary := false + for attr in field.attrs { + match attr.name { + 'skip' { + continue outer + } + 'primary' { + is_primary = true + } + else {} + } + } + mut stmt := '' + mut converted_typ := g.sql_type_from_v(typ, field.typ) + mut name := field.name + if converted_typ == '' { + if g.table.get_type_symbol(field.typ).kind == .struct_ { + converted_typ = g.sql_type_from_v(typ, ast.int_type) + g.sql_create_table(ast.SqlStmt{ + db_expr: node.db_expr + kind: node.kind + pos: node.pos + table_expr: ast.TypeNode{ + typ: field.typ + pos: node.table_expr.pos + } + }) + } else { + eprintln(g.table.get_type_symbol(field.typ).kind) + verror('unknown type ($field.typ)') + continue + } + } + stmt = '`$name` $converted_typ' + + if field.has_default_expr { + stmt += ' DEFAULT ' + stmt += field.default_expr.str() + } + if is_primary { + stmt += ' PRIMARY KEY' + } + fields << stmt + } + create_string += fields.join(', ') + create_string += ');' + g.write('sqlite__DB_exec(') + g.expr(node.db_expr) + g.writeln(', _SLIT("$create_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);') } @@ -345,6 +437,16 @@ 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) sqlite3_type_from_v(typ SqlType, v_typ ast.Type) string { + if v_typ.is_number() || v_typ == ast.bool_type { + return 'INTEGER' + } + if v_typ.is_string() { + return 'TEXT' + } + return '' +} + // mysql fn (mut g Gen) mysql_stmt(node ast.SqlStmt) { diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index f5f198b610..ccad27a929 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -127,6 +127,25 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { kind = .delete } else if n == 'update' { kind = .update + } else if n == 'create' { + kind = .create + table := p.check_name() + if table != 'table' { + p.error('expected `table` got `$table`') + return ast.SqlStmt{} + } + typ := p.parse_type() + typ_pos := p.tok.position() + p.check(.rcbr) + return ast.SqlStmt{ + db_expr: db_expr + kind: kind + pos: pos.extend(p.prev_tok.position()) + table_expr: ast.TypeNode{ + typ: typ + pos: typ_pos + } + } } mut inserted_var_name := '' mut table_type := ast.Type(0)