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:
parent
f1bda88964
commit
dcf4a6b008
@ -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]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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{
|
||||||
|
Loading…
Reference in New Issue
Block a user