1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

orm: add support for custom table names & custom field names (#9697)

This commit is contained in:
Louis Schmieder 2021-04-15 09:53:43 +02:00 committed by GitHub
parent f1bda88964
commit dcf4a6b008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 35 deletions

View File

@ -1,6 +1,7 @@
import sqlite
import mysql
[table: 'modules']
struct Module {
id int [primary; sql: serial]
name string
@ -11,8 +12,8 @@ struct Module {
struct User {
id int [primary; sql: serial]
age int [unique: 'user']
name string [unique]
is_customer bool [unique: 'user']
name string [sql: 'username'; unique]
is_customer bool [sql: 'abc'; unique: 'user']
skipped_string string [skip]
}

View File

@ -2,14 +2,18 @@
## Attributes
### Structs
- `[tablename: 'name']` sets a custom table name
### Fields
- `[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`)
- `[sql: 'name']` sets a custom column name for the field
## Usage

View File

@ -9,10 +9,11 @@ struct Module {
nr_downloads int
}
[table: 'userlist']
struct User {
id int [primary]
id int [primary; sql: serial]
age int
name string
name string [sql: 'username']
is_customer bool
skipped_string string [skip]
}
@ -28,9 +29,9 @@ fn test_orm_sqlite() {
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)")
db.exec("insert into User (name, age, is_customer) values ('Kate', 30, 1)")
db.exec("insert into userlist (username, age) values ('Sam', 29)")
db.exec("insert into userlist (username, age) values ('Peter', 31)")
db.exec("insert into userlist (username, age, is_customer) values ('Kate', 30, 1)")
c := sql db {
select count from User where id != 1
@ -144,6 +145,7 @@ fn test_orm_sqlite() {
sql db {
update User set age = 32, name = 'Kate N' where name == 'Kate'
}
mut kate3 := sql db {
select from User where id == 3
}

View File

@ -135,7 +135,7 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) {
if node.kind == .insert {
// build the object now (`x.name = ... x.id == ...`)
for i, field in node.fields {
if field.name == 'id' {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
x := '${node.object_var_name}.$field.name'
@ -319,7 +319,7 @@ fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) {
}
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)
table_name := g.get_table_name(node.table_expr)
g.writeln('// sqlite3 table drop')
create_string := 'DROP TABLE $table_name;'
g.write('sqlite__DB_exec(')
@ -381,7 +381,7 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmt, typ SqlType) {
g.writeln('memset($bind, 0, sizeof(MYSQL_BIND)*$g.sql_i);')
if node.kind == .insert {
for i, field in node.fields {
if field.name == 'id' {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.writeln('//$field.name ($field.typ)')
@ -613,7 +613,7 @@ fn (mut g Gen) mysql_create_table(node ast.SqlStmt, typ SqlType) {
}
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)
table_name := g.get_table_name(node.table_expr)
g.writeln('// mysql table drop')
create_string := 'DROP TABLE $table_name;'
tmp := g.new_tmp_var()
@ -747,14 +747,14 @@ fn (mut g Gen) sql_expr_defaults(node ast.SqlExpr, sql_typ SqlType) {
fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr) string {
mut sql_query := 'SELECT '
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
table_name := g.get_table_name(node.table_expr)
if node.is_count {
// `select count(*) from User`
sql_query += 'COUNT(*) FROM `$table_name` '
} else {
// `select id, name, country from User`
for i, field in node.fields {
sql_query += '`$field.name`'
sql_query += '`${g.get_field_name(field)}`'
if i < node.fields.len - 1 {
sql_query += ', '
}
@ -768,7 +768,7 @@ fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr) string {
}
fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
table_name := g.get_table_name(node.table_expr)
if node.kind == .insert {
g.write('INSERT INTO `$table_name` (')
} else if node.kind == .update {
@ -778,17 +778,17 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
}
if node.kind == .insert {
for i, field in node.fields {
if field.name == 'id' {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.write('`$field.name`')
g.write('`${g.get_field_name(field)}`')
if i < node.fields.len - 1 {
g.write(', ')
}
}
g.write(') values (')
for i, field in node.fields {
if field.name == 'id' {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
if typ == .sqlite3 {
@ -804,7 +804,7 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
g.write(')')
} else if node.kind == .update {
for i, col in node.updated_columns {
g.write(' $col = ')
g.write(' ${g.get_field_name(g.get_struct_field(col))} = ')
g.expr_to_sql(node.update_exprs[i], typ)
if i < node.updated_columns.len - 1 {
g.write(', ')
@ -822,11 +822,8 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
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')
}
struct_data := typ_sym.info as ast.Struct
table_name := typ_sym.name.split('.').last()
struct_data := typ_sym.struct_info()
table_name := g.get_table_name(node.table_expr)
mut create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` ('
mut fields := []string{}
@ -835,18 +832,20 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
mut unique := map[string][]string{}
for field in struct_data.fields {
name := g.get_field_name(field)
mut is_primary := false
mut no_null := false
mut is_unique := false
mut is_skip := false
for attr in field.attrs {
match attr.name {
'primary' {
is_primary = true
primary = field.name
primary = name
}
'unique' {
if attr.arg != '' {
unique[attr.arg] << field.name
unique[attr.arg] << name
} else {
is_unique = true
}
@ -854,12 +853,17 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
'nonull' {
no_null = true
}
'skip' {
is_skip = true
}
else {}
}
}
if is_skip {
continue
}
mut stmt := ''
mut converted_typ := g.sql_type_from_v(typ, g.get_sql_field_type(field))
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)
@ -873,8 +877,7 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
}
})
} else {
eprintln(g.table.get_type_symbol(field.typ).kind)
verror('unknown type ($field.typ)')
verror('unknown type ($field.typ) for field $field.name in struct $table_name')
continue
}
}
@ -963,7 +966,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
if g.sql_side == .left {
// println("sql gen left $expr.name")
g.sql_left_type = g.get_struct_field_typ(expr.name)
g.write(expr.name)
g.write(g.get_field_name(g.get_struct_field(expr.name)))
} else {
g.inc_sql_i(typ)
info := expr.info as ast.IdentVar
@ -1071,7 +1074,7 @@ fn (mut g Gen) parse_db_from_type_string(name string) SqlType {
fn (mut g Gen) get_sql_field_type(field ast.StructField) ast.Type {
mut typ := field.typ
for attr in field.attrs {
if attr.name == 'sql' && attr.arg != '' {
if attr.name == 'sql' && !attr.is_string_arg && attr.arg != '' {
if attr.arg.to_lower() == 'serial' {
typ = ast.Type(-1)
break
@ -1081,3 +1084,37 @@ fn (mut g Gen) get_sql_field_type(field ast.StructField) ast.Type {
}
return typ
}
fn (mut g Gen) get_table_name(table_expr ast.TypeNode) string {
info := g.table.get_type_symbol(table_expr.typ).struct_info()
mut tablename := util.strip_mod_name(g.table.get_type_symbol(table_expr.typ).name)
for attr in info.attrs {
if attr.name == 'table' && attr.is_string_arg && attr.arg != '' {
tablename = attr.arg
break
}
}
return tablename
}
fn (mut g Gen) get_struct_field(name string) ast.StructField {
info := g.table.get_type_symbol(g.table.type_idxs[g.sql_table_name]).struct_info()
mut f := ast.StructField{}
for field in info.fields {
if field.name == name {
f = field
}
}
return f
}
fn (mut g Gen) get_field_name(field ast.StructField) string {
mut name := field.name
for attr in field.attrs {
if attr.name == 'sql' && attr.is_string_arg && attr.arg != '' {
name = attr.arg
break
}
}
return name
}

View File

@ -1,19 +1,20 @@
import sqlite
struct Upper {
id int
id int [primary; sql: serial]
sub SubStruct
}
struct SubStruct {
id int
id int [primary; sql: serial]
name string
}
fn test_orm_sub_structs() {
db := sqlite.connect(':memory:') or { panic(err) }
db.exec('create table Upper (id integer primary key, sub int default 0)')
db.exec('create table SubStruct (id integer primary key, name string default "")')
sql db {
create table Upper
}
upper_1 := Upper{
sub: SubStruct{