From 67d8639917d627a8d9d0a79f1973d495a4035974 Mon Sep 17 00:00:00 2001 From: Louis Schmieder Date: Sun, 11 Apr 2021 23:57:25 +0200 Subject: [PATCH] orm: add unique fields & add drop table stmt (#9684) --- examples/database/orm.v | 11 +++++--- vlib/orm/README.md | 12 +++++++- vlib/v/ast/ast.v | 1 + vlib/v/fmt/fmt.v | 3 ++ vlib/v/gen/c/sql.v | 62 +++++++++++++++++++++++++++++++++++++++-- vlib/v/parser/sql.v | 19 +++++++++++++ 6 files changed, 101 insertions(+), 7 deletions(-) diff --git a/examples/database/orm.v b/examples/database/orm.v index 7cc1fb847f..dd52cfc6e6 100644 --- a/examples/database/orm.v +++ b/examples/database/orm.v @@ -10,15 +10,14 @@ struct Module { struct User { id int [primary; sql: serial] - age int - name string [nonull] - is_customer bool + age int [unique: 'user'] + name string [unique] + is_customer bool [unique: 'user'] skipped_string string [skip] } fn main() { db := sqlite.connect(':memory:') or { panic(err) } - db.exec('drop table if exists User') sql db { create table Module } @@ -40,6 +39,10 @@ fn main() { select from Module where id == 1 } + sql db { + drop table Module + } + eprintln(modul) mysql() diff --git a/vlib/orm/README.md b/vlib/orm/README.md index 0293676cd2..61556c1cf2 100644 --- a/vlib/orm/README.md +++ b/vlib/orm/README.md @@ -4,7 +4,9 @@ ### Fields -- `[primary]` set the field as the primary key +- `[primary]` sets the field as the primary key +- `[unique]` sets the field as unique +- `[unique: 'foo']` adds the field to a unique group - `[nonull]` field will be `NOT NULL` in table creation - `[skip]` field will be skipped - `[sql: type]` sets the type which is used in sql (special type `serial`) @@ -26,6 +28,14 @@ sql db { } ``` +### Drop + +```v ignore +sql db { + drop table Foo +} +``` + ### Insert ```v ignore diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 89ca2504e3..b00fbde031 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1407,6 +1407,7 @@ pub enum SqlStmtKind { update delete create + drop } pub struct SqlStmt { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 4fe58dc23f..a1419f1ae5 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1237,6 +1237,9 @@ pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) { .create { f.writeln('create table $table_name') } + .drop { + f.writeln('drop table $table_name') + } } f.writeln('}') } diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index f147093798..641d15f84c 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -27,6 +27,9 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { if node.kind == .create { g.sql_create_table(node) return + } else if node.kind == .drop { + g.sql_drop_table(node) + return } g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name typ := g.parse_db_type(node.db_expr) @@ -58,6 +61,21 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmt) { } } +fn (mut g Gen) sql_drop_table(node ast.SqlStmt) { + typ := g.parse_db_type(node.db_expr) + match typ { + .sqlite3 { + g.sqlite3_drop_table(node, typ) + } + .mysql { + g.mysql_drop_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) { g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name typ := g.parse_db_type(node.db_expr) @@ -300,6 +318,15 @@ fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) { g.writeln(', _SLIT("$create_string"));') } +fn (mut g Gen) sqlite3_drop_table(node ast.SqlStmt, typ SqlType) { + table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name) + g.writeln('// sqlite3 table drop') + create_string := 'DROP TABLE $table_name;' + g.write('sqlite__DB_exec(') + g.expr(node.db_expr) + g.writeln(', _SLIT("$create_string"));') +} + fn (mut g Gen) sqlite3_bind(val string, len string, typ ast.Type) { match g.sqlite3_type_from_v(typ) { 'INTEGER' { @@ -582,7 +609,18 @@ fn (mut g Gen) mysql_create_table(node ast.SqlStmt, typ SqlType) { g.write('Option_mysql__Result $tmp = mysql__Connection_query(&') g.expr(node.db_expr) g.writeln(', _SLIT("$create_string"));') - g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; _STR("Something went wrong\\000%.*s", 2, IError_str(err)); }') + g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }') +} + +fn (mut g Gen) mysql_drop_table(node ast.SqlStmt, typ SqlType) { + table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name) + g.writeln('// mysql table drop') + create_string := 'DROP TABLE $table_name;' + tmp := g.new_tmp_var() + g.write('Option_mysql__Result $tmp = mysql__Connection_query(&') + g.expr(node.db_expr) + g.writeln(', _SLIT("$create_string"));') + g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }') } fn (mut g Gen) mysql_bind(val string, _ ast.Type) { @@ -794,16 +832,25 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string { mut fields := []string{} mut primary := '' // for mysql + mut unique := map[string][]string{} for field in struct_data.fields { mut is_primary := false mut no_null := false + mut is_unique := false for attr in field.attrs { match attr.name { 'primary' { is_primary = true primary = field.name } + 'unique' { + if attr.arg != '' { + unique[attr.arg] << field.name + } else { + is_unique = true + } + } 'nonull' { no_null = true } @@ -840,11 +887,23 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string { if no_null { stmt += ' NOT NULL' } + if is_unique { + stmt += ' UNIQUE' + } if is_primary && typ == .sqlite3 { stmt += ' PRIMARY KEY' } fields << stmt } + if unique.len > 0 { + for k, v in unique { + mut tmp := []string{} + for f in v { + tmp << '`$f`' + } + fields << '/* $k */UNIQUE(${tmp.join(', ')})' + } + } if typ == .mysql { fields << 'PRIMARY KEY(`$primary`)' } @@ -903,7 +962,6 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) { // for left sides just add a string, for right sides, generate the bindings if g.sql_side == .left { // println("sql gen left $expr.name") - eprintln(expr.name) g.sql_left_type = g.get_struct_field_typ(expr.name) g.write(expr.name) } else { diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index ccad27a929..34d412cad7 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -146,6 +146,25 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { pos: typ_pos } } + } else if n == 'drop' { + kind = .drop + 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)