mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
orm: allow structs without the id field, more flexible primary keys (#18140)
This commit is contained in:

committed by
GitHub

parent
72b2f22057
commit
6ac09e605e
@@ -116,12 +116,13 @@ fn (kind OrderType) to_str() string {
|
||||
// parentheses defines which fields will be inside ()
|
||||
pub struct QueryData {
|
||||
pub:
|
||||
fields []string
|
||||
data []Primitive
|
||||
types []int
|
||||
parentheses [][]int
|
||||
kinds []OperationKind
|
||||
is_and []bool
|
||||
fields []string
|
||||
data []Primitive
|
||||
types []int
|
||||
parentheses [][]int
|
||||
kinds []OperationKind
|
||||
primary_column_name string
|
||||
is_and []bool
|
||||
}
|
||||
|
||||
pub struct InfixType {
|
||||
@@ -202,7 +203,18 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
|
||||
mut select_fields := []string{}
|
||||
|
||||
for i in 0 .. data.fields.len {
|
||||
column_name := data.fields[i]
|
||||
is_primary_column := column_name == data.primary_column_name
|
||||
|
||||
if data.data.len > 0 {
|
||||
// Allow the database to insert an automatically generated primary key
|
||||
// under the hood if it is not passed by the user.
|
||||
if is_primary_column && data.data[i].type_idx() in orm.nums {
|
||||
if (data.data[i] as int) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
match data.data[i].type_name() {
|
||||
'string' {
|
||||
if (data.data[i] as string).len == 0 {
|
||||
@@ -218,9 +230,9 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
|
||||
}
|
||||
data_data << data.data[i]
|
||||
}
|
||||
select_fields << '${q}${data.fields[i]}${q}'
|
||||
select_fields << '${q}${column_name}${q}'
|
||||
values << factory_insert_qm_value(num, qm, c)
|
||||
data_fields << data.fields[i]
|
||||
data_fields << column_name
|
||||
c++
|
||||
}
|
||||
|
||||
@@ -313,6 +325,7 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
|
||||
$if trace_orm ? {
|
||||
eprintln('> orm: ${str}')
|
||||
}
|
||||
|
||||
return str, QueryData{
|
||||
fields: data_fields
|
||||
data: data_data
|
||||
@@ -519,9 +532,7 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
|
||||
}
|
||||
fs << stmt
|
||||
}
|
||||
if primary == '' {
|
||||
return error('A primary key is required for ${table}')
|
||||
}
|
||||
|
||||
if unique.len > 0 {
|
||||
for k, v in unique {
|
||||
mut tmp := []string{}
|
||||
@@ -531,7 +542,11 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
|
||||
fs << '/* ${k} */UNIQUE(${tmp.join(', ')})'
|
||||
}
|
||||
}
|
||||
fs << 'PRIMARY KEY(${q}${primary}${q})'
|
||||
|
||||
if primary != '' {
|
||||
fs << 'PRIMARY KEY(${q}${primary}${q})'
|
||||
}
|
||||
|
||||
fs << unique_fields
|
||||
str += fs.join(', ')
|
||||
str += ');'
|
||||
@@ -541,6 +556,7 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
|
||||
$if trace_orm ? {
|
||||
eprintln('> orm: ${str}')
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
|
@@ -18,8 +18,53 @@ mut:
|
||||
owner_id int
|
||||
}
|
||||
|
||||
struct Entity {
|
||||
name string [primary]
|
||||
description string
|
||||
}
|
||||
|
||||
fn test_create_without_id_field() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
|
||||
sql db {
|
||||
create table Entity
|
||||
}!
|
||||
|
||||
first := Entity{
|
||||
name: 'First'
|
||||
description: 'Such wow! No `id` field'
|
||||
}
|
||||
second := Entity{
|
||||
name: 'Second'
|
||||
description: 'Such wow! No `id` field again'
|
||||
}
|
||||
|
||||
sql db {
|
||||
insert first into Entity
|
||||
insert second into Entity
|
||||
}!
|
||||
|
||||
entities := sql db {
|
||||
select from Entity
|
||||
}!
|
||||
|
||||
assert entities.len == 2
|
||||
|
||||
first_entity := sql db {
|
||||
select from Entity where name == 'First'
|
||||
}!
|
||||
|
||||
assert first_entity.first().name == 'First'
|
||||
|
||||
second_entity := sql db {
|
||||
select from Entity where name == 'Second'
|
||||
}!
|
||||
|
||||
assert second_entity.first().name == 'Second'
|
||||
}
|
||||
|
||||
fn test_create_only_one_table() {
|
||||
mut db := sqlite.connect(':memory:') or { panic(err) }
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
|
||||
sql db {
|
||||
create table Parent
|
||||
@@ -47,7 +92,7 @@ fn test_create_only_one_table() {
|
||||
}
|
||||
|
||||
fn test_drop_only_one_table() {
|
||||
mut db := sqlite.connect(':memory:') or { panic(err) }
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
|
||||
sql db {
|
||||
create table Parent
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import db.sqlite
|
||||
import rand
|
||||
|
||||
struct Parent {
|
||||
id int [primary; sql: serial]
|
||||
@@ -37,12 +38,106 @@ pub mut:
|
||||
username string [unique]
|
||||
}
|
||||
|
||||
struct Entity {
|
||||
uuid string [primary]
|
||||
description string
|
||||
}
|
||||
|
||||
struct EntityWithFloatPrimary {
|
||||
id f64 [primary]
|
||||
name string
|
||||
}
|
||||
|
||||
pub fn insert_parent(db sqlite.DB, mut parent Parent) ! {
|
||||
sql db {
|
||||
insert parent into Parent
|
||||
}!
|
||||
}
|
||||
|
||||
fn test_set_primary_value() {
|
||||
// The primary key is an constraint that ensures each record in a table is unique.
|
||||
// Primary keys must contain unique values and cannot contain `NULL` values.
|
||||
// However, this statement does not imply that a value cannot be inserted by the user.
|
||||
// Therefore, let's allow this.
|
||||
db := sqlite.connect(':memory:')!
|
||||
|
||||
sql db {
|
||||
create table Child
|
||||
}!
|
||||
|
||||
child := Child{
|
||||
id: 10
|
||||
parent_id: 20
|
||||
}
|
||||
|
||||
sql db {
|
||||
insert child into Child
|
||||
}!
|
||||
|
||||
children := sql db {
|
||||
select from Child
|
||||
}!
|
||||
|
||||
assert children.first() == child
|
||||
}
|
||||
|
||||
fn test_uuid_primary_key() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
uuid := rand.uuid_v4()
|
||||
|
||||
sql db {
|
||||
create table Entity
|
||||
}!
|
||||
|
||||
entity := Entity{
|
||||
uuid: uuid
|
||||
description: 'Test'
|
||||
}
|
||||
|
||||
sql db {
|
||||
insert entity into Entity
|
||||
}!
|
||||
|
||||
entities := sql db {
|
||||
select from Entity where uuid == uuid
|
||||
}!
|
||||
|
||||
mut is_duplicate_inserted := true
|
||||
|
||||
sql db {
|
||||
insert entity into Entity
|
||||
} or { is_duplicate_inserted = false }
|
||||
|
||||
assert entities.len == 1
|
||||
assert entities.first() == entity
|
||||
assert is_duplicate_inserted == false
|
||||
}
|
||||
|
||||
fn test_float_primary_key() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
id := 3.14
|
||||
|
||||
sql db {
|
||||
create table EntityWithFloatPrimary
|
||||
}!
|
||||
|
||||
entity := EntityWithFloatPrimary{
|
||||
id: id
|
||||
name: 'Test'
|
||||
}
|
||||
|
||||
sql db {
|
||||
insert entity into EntityWithFloatPrimary
|
||||
}!
|
||||
|
||||
entities := sql db {
|
||||
select from EntityWithFloatPrimary where id == id
|
||||
}!
|
||||
|
||||
assert entities.len == 1
|
||||
assert entities.first() == entity
|
||||
}
|
||||
|
||||
fn test_does_not_insert_uninitialized_field() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
|
||||
|
Reference in New Issue
Block a user