From e1a5acc1efb9ffd6ac0f7f80546c04ead45118f7 Mon Sep 17 00:00:00 2001 From: Alexander Mandrikov Date: Sat, 9 Nov 2024 22:26:22 +0700 Subject: [PATCH] orm: support plain `@[serial]` attribute for marking struct fields (#22814) --- cmd/tools/vtest-self.v | 2 + doc/docs.md | 2 +- vlib/orm/README.md | 2 +- vlib/orm/orm.v | 9 +++- vlib/orm/orm_serial_attribute_test.v | 67 ++++++++++++++++++++++++++++ vlib/v/gen/c/orm.v | 2 + 6 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 vlib/orm/orm_serial_attribute_test.v diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 04a4da738f..5bc0cdd28f 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -166,6 +166,7 @@ const skip_with_fsanitize_memory = [ 'vlib/orm/orm_option_array_test.v', 'vlib/orm/orm_option_time_test.v', 'vlib/orm/orm_order_by_custom_field_test.v', + 'vlib/orm/orm_serial_attribute_test.v', 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', 'vlib/db/sqlite/sqlite_comptime_field_test.v', @@ -263,6 +264,7 @@ const skip_on_ubuntu_musl = [ 'vlib/orm/orm_option_array_test.v', 'vlib/orm/orm_option_time_test.v', 'vlib/orm/orm_order_by_custom_field_test.v', + 'vlib/orm/orm_serial_attribute_test.v', 'vlib/v/tests/orm_enum_test.v', 'vlib/v/tests/orm_sub_struct_test.v', 'vlib/v/tests/orm_sub_array_struct_test.v', diff --git a/doc/docs.md b/doc/docs.md index 5c1440b83d..c4a4532fcc 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -5205,7 +5205,7 @@ import db.sqlite // sets a custom table name. Default is struct name (case-sensitive) @[table: 'customers'] struct Customer { - id int @[primary; sql: serial] // a field named `id` of integer type must be the first field + id int @[primary; serial] // a field named `id` of integer type must be the first field name string nr_orders int country ?string diff --git a/vlib/orm/README.md b/vlib/orm/README.md index 56352f4c41..5252f89fd5 100644 --- a/vlib/orm/README.md +++ b/vlib/orm/README.md @@ -28,7 +28,7 @@ struct Foo { - `[unique: 'foo']` adds the field to a `UNIQUE` group - `[skip]` or `[sql: '-']` field will be skipped - `[sql: type]` where `type` is a V type such as `int` or `f64` -- `[sql: serial]` lets the DB backend choose a column type for an auto-increment field +- `[serial]` or `[sql: serial]` lets the DB backend choose a column type for an auto-increment field - `[sql: 'name']` sets a custom column name for the field - `[sql_type: 'SQL TYPE']` explicitly sets the type in SQL - `[default: 'raw_sql']` inserts `raw_sql` verbatim in a "DEFAULT" clause when diff --git a/vlib/orm/orm.v b/vlib/orm/orm.v index abc383de39..642e164cb7 100644 --- a/vlib/orm/orm.v +++ b/vlib/orm/orm.v @@ -473,7 +473,7 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, } } 'primary' { - primary = field.name + primary = field_name primary_typ = field.typ } 'unique' { @@ -592,7 +592,14 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, fn sql_field_type(field TableField) int { mut typ := field.typ for attr in field.attrs { + // @[serial] + if attr.name == 'serial' && attr.kind == .plain && !attr.has_arg { + typ = serial + break + } + if attr.kind == .plain && attr.name == 'sql' && attr.arg != '' { + // @[sql: serial] if attr.arg.to_lower() == 'serial' { typ = serial break diff --git a/vlib/orm/orm_serial_attribute_test.v b/vlib/orm/orm_serial_attribute_test.v new file mode 100644 index 0000000000..0c30dc20e3 --- /dev/null +++ b/vlib/orm/orm_serial_attribute_test.v @@ -0,0 +1,67 @@ +import db.sqlite + +struct PlainNoArg { + id int @[primary; serial; sql: 'custom_id'] + name string +} + +fn test_plain_no_arg() { + mut db := sqlite.connect(':memory:')! + defer { db.close() or {} } + + sql db { + create table PlainNoArg + }! + + first := PlainNoArg{ + name: 'first' + } + second := PlainNoArg{ + name: 'second' + } + + sql db { + insert first into PlainNoArg + insert second into PlainNoArg + }! + + rows := sql db { + select from PlainNoArg order by id desc + }! + + assert rows[0].id == 2 + assert rows[0].name == 'second' +} + +struct SqlSerial { + id int @[primary; sql: serial] + name string +} + +fn test_sql_serial() { + mut db := sqlite.connect(':memory:')! + defer { db.close() or {} } + + sql db { + create table SqlSerial + }! + + first := SqlSerial{ + name: 'first' + } + second := SqlSerial{ + name: 'second' + } + + sql db { + insert first into SqlSerial + insert second into SqlSerial + }! + + rows := sql db { + select from SqlSerial order by id desc + }! + + assert rows[0].id == 2 + assert rows[0].name == 'second' +} diff --git a/vlib/v/gen/c/orm.v b/vlib/v/gen/c/orm.v index a77e487aeb..b64eb9403a 100644 --- a/vlib/v/gen/c/orm.v +++ b/vlib/v/gen/c/orm.v @@ -1274,6 +1274,8 @@ fn get_auto_field_idxs(fields []ast.StructField) []int { ret << i } else if attr.name == 'sql' && attr.arg == 'serial' { ret << i + } else if attr.name == 'serial' && attr.kind == .plain && !attr.has_arg { + ret << i } } }