Skip to content

Commit 5e69315

Browse files
authored
orm: add function call based builder API for dynamic queries (fix #24178) (#24196)
1 parent d559a62 commit 5e69315

5 files changed

Lines changed: 1059 additions & 3 deletions

File tree

‎cmd/tools/vtest-self.v‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ const skip_with_fsanitize_memory = [
156156
'vlib/orm/orm_order_by_custom_field_test.v',
157157
'vlib/orm/orm_serial_attribute_test.v',
158158
'vlib/orm/orm_option_subselect_test.v',
159+
'vlib/orm/orm_func_test.v',
159160
'vlib/db/sqlite/sqlite_test.v',
160161
'vlib/db/sqlite/sqlite_orm_test.v',
161162
'vlib/db/sqlite/sqlite_comptime_field_test.v',
@@ -246,6 +247,7 @@ const skip_on_ubuntu_musl = [
246247
'vlib/orm/orm_order_by_custom_field_test.v',
247248
'vlib/orm/orm_serial_attribute_test.v',
248249
'vlib/orm/orm_option_subselect_test.v',
250+
'vlib/orm/orm_func_test.v',
249251
'vlib/v/tests/orm_enum_test.v',
250252
'vlib/v/tests/orm_sub_struct_test.v',
251253
'vlib/v/tests/orm_sub_array_struct_test.v',

‎vlib/orm/README.md‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ struct Foo {
3838
- `[fkey: 'parent_id']` sets foreign key for an field which holds an array
3939

4040
## Usage
41+
> [!NOTE]
42+
> For using the Function Call API for `orm`, please check [`Function Call API`](#function-call-api).
4143
4244
Here are a couple example structs showing most of the features outlined above.
4345

@@ -246,3 +248,106 @@ fn main() {
246248
}!
247249
}
248250
```
251+
252+
## Function Call API
253+
You can utilize the `Function Call API` to work with `ORM`. It provides the
254+
capability to dynamically construct SQL statements. The Function Call API
255+
supports common operations such as `Create Table`/`Drop Table`/`Insert`/`Delete`/`Update`/`Select`,
256+
and offers convenient yet powerful features for constructing `WHERE` clauses,
257+
`SET` clauses, `SELECT` clauses, and more.
258+
259+
A complete example is available [here](https://github.com/vlang/v/blob/master/vlib/orm/orm_func_test.v).
260+
261+
Below, we illustrate its usage through several examples.
262+
263+
​​1. Define your struct​​ with the same method definitions as before:
264+
265+
```v ignore
266+
@[table: 'sys_users']
267+
struct User {
268+
id int @[primary;serial]
269+
name string
270+
age int
271+
role string
272+
status int
273+
salary int
274+
title string
275+
score int
276+
created_at ?time.Time @[sql_type: 'TIMESTAMP']
277+
}
278+
```
279+
280+
​​2. Create a database connection​​:
281+
282+
```v ignore
283+
mut db := sqlite.connect(':memory:')!
284+
defer { db.close() or {} }
285+
```
286+
287+
3. Create a `QueryBuilder`​​ (which also completes struct mapping):
288+
289+
```v ignore
290+
mut qb := orm.new_query[User](db)
291+
```
292+
293+
4. Create a database table​​:
294+
295+
```v ignore
296+
qb.create()!
297+
```
298+
299+
5. Insert multiple records​​ into the table:
300+
301+
```v ignore
302+
qb.insert_many(users)!
303+
```
304+
305+
6. Delete records​​ (note: `delete()` must follow `where()`):
306+
307+
```v ignore
308+
qb.where('name = ?','John')!.delete()!
309+
```
310+
311+
7. Query records​​ (you can specify fields of interest via `select`):
312+
313+
```v ignore
314+
// Returns []User with only 'name' populated; other fields are zero values.
315+
only_names := qb.select('name')!.query()!
316+
```
317+
318+
8. Update records​​ (note: `update()` must be placed last):
319+
320+
```v ignore
321+
qb.set('age = ?, title = ?', 71, 'boss')!.where('name = ?','John')!.update()!
322+
```
323+
324+
9. Drop the table​​:
325+
326+
```v ignore
327+
qb.drop()!
328+
```
329+
330+
10. Chainable method calls​​:
331+
Most Function Call API support chainable calls, allowing easy method chaining:
332+
333+
```v ignore
334+
final_users :=
335+
qb
336+
.drop()!
337+
.create()!
338+
.insert_many(users)!
339+
.set('name = ?', 'haha')!.where('name = ?', 'Tom')!.update()!
340+
.where('age >= ?', 30)!.delete()!
341+
.query()!
342+
```
343+
344+
11. Writing complex nested `WHERE` clauses​​:
345+
The API includes a built-in parser to handle intricate `WHERE` clause conditions. For example:
346+
347+
```v ignore
348+
where('created_at IS NULL && ((salary > ? && age < ?) || (role LIKE ?))', 2000, 30, '%employee%')!
349+
```
350+
351+
Note the use of placeholders `?`.
352+
The conditional expressions support logical operators including `AND`, `OR`, `||`, and `&&`.
353+

‎vlib/orm/orm.v‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ fn (kind OrderType) to_str() string {
131131
// parentheses defines which fields will be inside ()
132132
// auto_fields are indexes of fields where db should generate a value when absent in an insert
133133
pub struct QueryData {
134-
pub:
134+
pub mut:
135135
fields []string
136136
data []Primitive
137137
types []int
@@ -149,7 +149,7 @@ pub:
149149
}
150150

151151
pub struct TableField {
152-
pub:
152+
pub mut:
153153
name string
154154
typ int
155155
nullable bool
@@ -170,7 +170,7 @@ pub:
170170
// fields - Fields to select
171171
// types - Types to select
172172
pub struct SelectConfig {
173-
pub:
173+
pub mut:
174174
table string
175175
is_count bool
176176
has_where bool

0 commit comments

Comments
 (0)