orm: add unique fields & add drop table stmt (#9684)
							parent
							
								
									b0b3c51658
								
							
						
					
					
						commit
						67d8639917
					
				| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1407,6 +1407,7 @@ pub enum SqlStmtKind {
 | 
			
		|||
	update
 | 
			
		||||
	delete
 | 
			
		||||
	create
 | 
			
		||||
	drop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct SqlStmt {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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('}')
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue