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

pull/9738/head
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 sqlite
import mysql import mysql
[table: 'modules']
struct Module { struct Module {
id int [primary; sql: serial] id int [primary; sql: serial]
name string name string
@ -11,8 +12,8 @@ struct Module {
struct User { struct User {
id int [primary; sql: serial] id int [primary; sql: serial]
age int [unique: 'user'] age int [unique: 'user']
name string [unique] name string [sql: 'username'; unique]
is_customer bool [unique: 'user'] is_customer bool [sql: 'abc'; unique: 'user']
skipped_string string [skip] skipped_string string [skip]
} }

View File

@ -2,14 +2,18 @@
## Attributes ## Attributes
### Structs
- `[tablename: 'name']` sets a custom table name
### Fields ### Fields
- `[primary]` sets the field as the primary key - `[primary]` sets the field as the primary key
- `[unique]` sets the field as unique - `[unique]` sets the field as unique
- `[unique: 'foo']` adds the field to a unique group - `[unique: 'foo']` adds the field to a unique group
- `[nonull]` field will be `NOT NULL` in table creation
- `[skip]` field will be skipped - `[skip]` field will be skipped
- `[sql: type]` sets the type which is used in sql (special type `serial`) - `[sql: type]` sets the type which is used in sql (special type `serial`)
- `[sql: 'name']` sets a custom column name for the field
## Usage ## Usage

View File

@ -9,10 +9,11 @@ struct Module {
nr_downloads int nr_downloads int
} }
[table: 'userlist']
struct User { struct User {
id int [primary] id int [primary; sql: serial]
age int age int
name string name string [sql: 'username']
is_customer bool is_customer bool
skipped_string string [skip] skipped_string string [skip]
} }
@ -28,9 +29,9 @@ fn test_orm_sqlite() {
create table User create table User
} }
name := 'Peter' name := 'Peter'
db.exec("insert into User (name, age) values ('Sam', 29)") db.exec("insert into userlist (username, age) values ('Sam', 29)")
db.exec("insert into User (name, age) values ('Peter', 31)") db.exec("insert into userlist (username, age) values ('Peter', 31)")
db.exec("insert into User (name, age, is_customer) values ('Kate', 30, 1)") db.exec("insert into userlist (username, age, is_customer) values ('Kate', 30, 1)")
c := sql db { c := sql db {
select count from User where id != 1 select count from User where id != 1
@ -144,6 +145,7 @@ fn test_orm_sqlite() {
sql db { sql db {
update User set age = 32, name = 'Kate N' where name == 'Kate' update User set age = 32, name = 'Kate N' where name == 'Kate'
} }
mut kate3 := sql db { mut kate3 := sql db {
select from User where id == 3 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 { if node.kind == .insert {
// build the object now (`x.name = ... x.id == ...`) // build the object now (`x.name = ... x.id == ...`)
for i, field in node.fields { for i, field in node.fields {
if field.name == 'id' { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
x := '${node.object_var_name}.$field.name' 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) { 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') g.writeln('// sqlite3 table drop')
create_string := 'DROP TABLE $table_name;' create_string := 'DROP TABLE $table_name;'
g.write('sqlite__DB_exec(') 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);') g.writeln('memset($bind, 0, sizeof(MYSQL_BIND)*$g.sql_i);')
if node.kind == .insert { if node.kind == .insert {
for i, field in node.fields { for i, field in node.fields {
if field.name == 'id' { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
g.writeln('//$field.name ($field.typ)') 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) { 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') g.writeln('// mysql table drop')
create_string := 'DROP TABLE $table_name;' create_string := 'DROP TABLE $table_name;'
tmp := g.new_tmp_var() 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 { fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr) string {
mut sql_query := 'SELECT ' 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 { if node.is_count {
// `select count(*) from User` // `select count(*) from User`
sql_query += 'COUNT(*) FROM `$table_name` ' sql_query += 'COUNT(*) FROM `$table_name` '
} else { } else {
// `select id, name, country from User` // `select id, name, country from User`
for i, field in node.fields { for i, field in node.fields {
sql_query += '`$field.name`' sql_query += '`${g.get_field_name(field)}`'
if i < node.fields.len - 1 { if i < node.fields.len - 1 {
sql_query += ', ' 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) { 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 { if node.kind == .insert {
g.write('INSERT INTO `$table_name` (') g.write('INSERT INTO `$table_name` (')
} else if node.kind == .update { } else if node.kind == .update {
@ -778,17 +778,17 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
} }
if node.kind == .insert { if node.kind == .insert {
for i, field in node.fields { for i, field in node.fields {
if field.name == 'id' { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
g.write('`$field.name`') g.write('`${g.get_field_name(field)}`')
if i < node.fields.len - 1 { if i < node.fields.len - 1 {
g.write(', ') g.write(', ')
} }
} }
g.write(') values (') g.write(') values (')
for i, field in node.fields { for i, field in node.fields {
if field.name == 'id' { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
if typ == .sqlite3 { if typ == .sqlite3 {
@ -804,7 +804,7 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
g.write(')') g.write(')')
} else if node.kind == .update { } else if node.kind == .update {
for i, col in node.updated_columns { 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) g.expr_to_sql(node.update_exprs[i], typ)
if i < node.updated_columns.len - 1 { if i < node.updated_columns.len - 1 {
g.write(', ') 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 { fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
typ_sym := g.table.get_type_symbol(node.table_expr.typ) typ_sym := g.table.get_type_symbol(node.table_expr.typ)
if typ_sym.info !is ast.Struct { struct_data := typ_sym.struct_info()
verror('Type `$typ_sym.name` has to be a struct') table_name := g.get_table_name(node.table_expr)
}
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 create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` ('
mut fields := []string{} mut fields := []string{}
@ -835,18 +832,20 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
mut unique := map[string][]string{} mut unique := map[string][]string{}
for field in struct_data.fields { for field in struct_data.fields {
name := g.get_field_name(field)
mut is_primary := false mut is_primary := false
mut no_null := false mut no_null := false
mut is_unique := false mut is_unique := false
mut is_skip := false
for attr in field.attrs { for attr in field.attrs {
match attr.name { match attr.name {
'primary' { 'primary' {
is_primary = true is_primary = true
primary = field.name primary = name
} }
'unique' { 'unique' {
if attr.arg != '' { if attr.arg != '' {
unique[attr.arg] << field.name unique[attr.arg] << name
} else { } else {
is_unique = true is_unique = true
} }
@ -854,12 +853,17 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
'nonull' { 'nonull' {
no_null = true no_null = true
} }
'skip' {
is_skip = true
}
else {} else {}
} }
} }
if is_skip {
continue
}
mut stmt := '' mut stmt := ''
mut converted_typ := g.sql_type_from_v(typ, g.get_sql_field_type(field)) mut converted_typ := g.sql_type_from_v(typ, g.get_sql_field_type(field))
mut name := field.name
if converted_typ == '' { if converted_typ == '' {
if g.table.get_type_symbol(field.typ).kind == .struct_ { if g.table.get_type_symbol(field.typ).kind == .struct_ {
converted_typ = g.sql_type_from_v(typ, ast.int_type) 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 { } else {
eprintln(g.table.get_type_symbol(field.typ).kind) verror('unknown type ($field.typ) for field $field.name in struct $table_name')
verror('unknown type ($field.typ)')
continue continue
} }
} }
@ -963,7 +966,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
if g.sql_side == .left { if g.sql_side == .left {
// println("sql gen left $expr.name") // println("sql gen left $expr.name")
g.sql_left_type = g.get_struct_field_typ(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 { } else {
g.inc_sql_i(typ) g.inc_sql_i(typ)
info := expr.info as ast.IdentVar 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 { fn (mut g Gen) get_sql_field_type(field ast.StructField) ast.Type {
mut typ := field.typ mut typ := field.typ
for attr in field.attrs { 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' { if attr.arg.to_lower() == 'serial' {
typ = ast.Type(-1) typ = ast.Type(-1)
break break
@ -1081,3 +1084,37 @@ fn (mut g Gen) get_sql_field_type(field ast.StructField) ast.Type {
} }
return typ 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 import sqlite
struct Upper { struct Upper {
id int id int [primary; sql: serial]
sub SubStruct sub SubStruct
} }
struct SubStruct { struct SubStruct {
id int id int [primary; sql: serial]
name string name string
} }
fn test_orm_sub_structs() { fn test_orm_sub_structs() {
db := sqlite.connect(':memory:') or { panic(err) } db := sqlite.connect(':memory:') or { panic(err) }
db.exec('create table Upper (id integer primary key, sub int default 0)') sql db {
db.exec('create table SubStruct (id integer primary key, name string default "")') create table Upper
}
upper_1 := Upper{ upper_1 := Upper{
sub: SubStruct{ sub: SubStruct{