Skip to content

Commit d409721

Browse files
authored
breaking,orm: add table attrs; add table/field comment support for mysql and pg (#24744)
1 parent d52bac1 commit d409721

File tree

12 files changed

+376
-116
lines changed

12 files changed

+376
-116
lines changed

vlib/db/mysql/mysql_orm_test.v

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ struct TestDefaultAttribute {
3737
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
3838
}
3939

40+
@[comment: 'This is a table comment']
41+
struct TestCommentAttribute {
42+
id string @[primary; sql: serial]
43+
name string @[comment: 'real user name']
44+
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
45+
}
46+
4047
fn test_mysql_orm() {
4148
$if !network ? {
4249
eprintln('> Skipping test ${@FN}, since `-d network` is not passed.')
@@ -47,13 +54,17 @@ fn test_mysql_orm() {
4754
host: '127.0.0.1'
4855
port: 3306
4956
username: 'root'
50-
password: ''
57+
password: '12345678'
5158
dbname: 'mysql'
5259
)!
5360
defer {
5461
db.close()
5562
}
56-
db.create('Test', [
63+
table := orm.Table{
64+
name: 'Test'
65+
}
66+
db.drop(table) or {}
67+
db.create(table, [
5768
orm.TableField{
5869
name: 'id'
5970
typ: typeof[int]().idx
@@ -80,13 +91,13 @@ fn test_mysql_orm() {
8091
},
8192
]) or { panic(err) }
8293

83-
db.insert('Test', orm.QueryData{
94+
db.insert(table, orm.QueryData{
8495
fields: ['name', 'age']
8596
data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)]
8697
}) or { panic(err) }
8798

8899
res := db.select(orm.SelectConfig{
89-
table: 'Test'
100+
table: table
90101
has_where: true
91102
fields: ['id', 'name', 'age']
92103
types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx]
@@ -272,4 +283,49 @@ fn test_mysql_orm() {
272283
'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP'
273284
}]
274285
assert information_schema_column_default_sql == result_defaults.maps()
286+
287+
/** test comment attribute
288+
*/
289+
sql db {
290+
create table TestCommentAttribute
291+
}!
292+
293+
mut column_comments := db.query("
294+
SELECT COLUMN_COMMENT
295+
FROM INFORMATION_SCHEMA.COLUMNS
296+
WHERE TABLE_NAME = 'TestCommentAttribute'
297+
ORDER BY ORDINAL_POSITION
298+
") or {
299+
println(err)
300+
panic(err)
301+
}
302+
303+
mut table_comment := db.query("
304+
SELECT TABLE_COMMENT
305+
FROM INFORMATION_SCHEMA.TABLES
306+
WHERE TABLE_NAME = 'TestCommentAttribute'
307+
") or {
308+
println(err)
309+
panic(err)
310+
}
311+
312+
sql db {
313+
drop table TestCommentAttribute
314+
}!
315+
316+
information_schema_column_comment_sql := [{
317+
'COLUMN_COMMENT': ''
318+
}, {
319+
'COLUMN_COMMENT': 'real user name'
320+
}, {
321+
'COLUMN_COMMENT': ''
322+
}]
323+
assert information_schema_column_comment_sql == column_comments.maps()
324+
325+
information_schema_table_comment_sql := [
326+
{
327+
'TABLE_COMMENT': 'This is a table comment'
328+
},
329+
]
330+
assert information_schema_table_comment_sql == table_comment.maps()
275331
}

vlib/db/mysql/orm.c.v

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
122122
}
123123

124124
// insert is used internally by V's ORM for processing `INSERT ` queries
125-
pub fn (db DB) insert(table string, data orm.QueryData) ! {
126-
mut converted_primitive_array := db.convert_query_data_to_primitives(table, data)!
125+
pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
126+
mut converted_primitive_array := db.convert_query_data_to_primitives(table.name, data)!
127127

128128
converted_primitive_data := orm.QueryData{
129129
fields: data.fields
@@ -139,13 +139,13 @@ pub fn (db DB) insert(table string, data orm.QueryData) ! {
139139
}
140140

141141
// update is used internally by V's ORM for processing `UPDATE ` queries
142-
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
142+
pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
143143
query, _ := orm.orm_stmt_gen(.default, table, '`', .update, false, '?', 1, data, where)
144144
mysql_stmt_worker(db, query, data, where)!
145145
}
146146

147147
// delete is used internally by V's ORM for processing `DELETE ` queries
148-
pub fn (db DB) delete(table string, where orm.QueryData) ! {
148+
pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
149149
query, _ := orm.orm_stmt_gen(.default, table, '`', .delete, false, '?', 1, orm.QueryData{},
150150
where)
151151
mysql_stmt_worker(db, query, orm.QueryData{}, where)!
@@ -160,16 +160,15 @@ pub fn (db DB) last_id() int {
160160
}
161161

162162
// create is used internally by V's ORM for processing table creation queries (DDL)
163-
pub fn (db DB) create(table string, fields []orm.TableField) ! {
164-
query := orm.orm_table_gen(table, '`', true, 0, fields, mysql_type_from_v, false) or {
165-
return err
166-
}
163+
pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
164+
query := orm.orm_table_gen(.mysql, table, '`', true, 0, fields, mysql_type_from_v,
165+
false) or { return err }
167166
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
168167
}
169168

170169
// drop is used internally by V's ORM for processing table destroying queries (DDL)
171-
pub fn (db DB) drop(table string) ! {
172-
query := 'DROP TABLE `${table}`;'
170+
pub fn (db DB) drop(table orm.Table) ! {
171+
query := 'DROP TABLE `${table.name}`;'
173172
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
174173
}
175174

vlib/db/pg/orm.v

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,20 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
3131
// sql stmt
3232

3333
// insert is used internally by V's ORM for processing `INSERT ` queries
34-
pub fn (db DB) insert(table string, data orm.QueryData) ! {
34+
pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
3535
query, converted_data := orm.orm_stmt_gen(.default, table, '"', .insert, true, '$',
3636
1, data, orm.QueryData{})
3737
pg_stmt_worker(db, query, converted_data, orm.QueryData{})!
3838
}
3939

4040
// update is used internally by V's ORM for processing `UPDATE ` queries
41-
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
41+
pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
4242
query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data, where)
4343
pg_stmt_worker(db, query, data, where)!
4444
}
4545

4646
// delete is used internally by V's ORM for processing `DELETE ` queries
47-
pub fn (db DB) delete(table string, where orm.QueryData) ! {
47+
pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
4848
query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{},
4949
where)
5050
pg_stmt_worker(db, query, orm.QueryData{}, where)!
@@ -60,14 +60,21 @@ pub fn (db DB) last_id() int {
6060
// DDL (table creation/destroying etc)
6161

6262
// create is used internally by V's ORM for processing table creation queries (DDL)
63-
pub fn (db DB) create(table string, fields []orm.TableField) ! {
64-
query := orm.orm_table_gen(table, '"', true, 0, fields, pg_type_from_v, false) or { return err }
65-
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
63+
pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
64+
query := orm.orm_table_gen(.pg, table, '"', true, 0, fields, pg_type_from_v, false) or {
65+
return err
66+
}
67+
stmts := query.split(';')
68+
for stmt in stmts {
69+
if stmt != '' {
70+
pg_stmt_worker(db, stmt + ';', orm.QueryData{}, orm.QueryData{})!
71+
}
72+
}
6673
}
6774

6875
// drop is used internally by V's ORM for processing table destroying queries (DDL)
69-
pub fn (db DB) drop(table string) ! {
70-
query := 'DROP TABLE "${table}";'
76+
pub fn (db DB) drop(table orm.Table) ! {
77+
query := 'DROP TABLE "${table.name}";'
7178
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
7279
}
7380

vlib/db/pg/pg_orm_test.v

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ struct TestDefaultAttribute {
3636
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
3737
}
3838

39+
@[comment: 'This is a table comment']
40+
struct TestCommentAttribute {
41+
id string @[primary; sql: serial]
42+
name string @[comment: 'real user name']
43+
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
44+
}
45+
3946
fn test_pg_orm() {
4047
$if !network ? {
4148
eprintln('> Skipping test ${@FN}, since `-d network` is not passed.')
@@ -46,15 +53,18 @@ fn test_pg_orm() {
4653
host: 'localhost'
4754
user: 'postgres'
4855
password: '12345678'
49-
dbname: 'test'
56+
dbname: 'postgres'
5057
) or { panic(err) }
5158

5259
defer {
5360
db.close()
5461
}
55-
db.drop('Test')!
62+
table := orm.Table{
63+
name: 'Test'
64+
}
65+
db.drop(table) or {}
5666

57-
db.create('Test', [
67+
db.create(table, [
5868
orm.TableField{
5969
name: 'id'
6070
typ: typeof[string]().idx
@@ -94,13 +104,13 @@ fn test_pg_orm() {
94104
},
95105
]) or { panic(err) }
96106

97-
db.insert('Test', orm.QueryData{
107+
db.insert(table, orm.QueryData{
98108
fields: ['name', 'age']
99109
data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)]
100110
}) or { panic(err) }
101111

102112
res := db.select(orm.SelectConfig{
103-
table: 'Test'
113+
table: table
104114
is_count: false
105115
has_where: true
106116
has_order: false
@@ -143,7 +153,7 @@ fn test_pg_orm() {
143153
*/
144154
sql db {
145155
drop table TestCustomSqlType
146-
}!
156+
} or {}
147157

148158
sql db {
149159
create table TestCustomSqlType
@@ -232,4 +242,60 @@ fn test_pg_orm() {
232242
drop table TestDefaultAttribute
233243
}!
234244
assert ['gen_random_uuid()', '', 'CURRENT_TIMESTAMP'] == information_schema_defaults_results
245+
246+
/** test comment attribute
247+
*/
248+
sql db {
249+
create table TestCommentAttribute
250+
}!
251+
252+
mut column_comments := db.exec("
253+
SELECT
254+
a.attname AS column_name,
255+
col_description(a.attrelid, a.attnum) AS column_comment
256+
FROM pg_attribute a
257+
JOIN pg_class c ON c.oid = a.attrelid
258+
JOIN pg_namespace n ON n.oid = c.relnamespace
259+
WHERE c.relname = 'TestCommentAttribute'
260+
AND n.nspname = 'public'
261+
AND a.attnum > 0
262+
AND NOT a.attisdropped
263+
ORDER BY a.attnum
264+
") or {
265+
println(err)
266+
panic(err)
267+
}
268+
269+
mut table_comment := db.exec("
270+
SELECT
271+
nspname AS schema_name,
272+
relname AS table_name,
273+
obj_description(pc.oid) AS table_comment
274+
FROM pg_class pc
275+
JOIN pg_namespace pn ON pn.oid = pc.relnamespace
276+
WHERE pc.relkind = 'r' AND pc.relname = 'TestCommentAttribute'
277+
ORDER BY schema_name, table_name
278+
") or {
279+
println(err)
280+
panic(err)
281+
}
282+
283+
sql db {
284+
drop table TestCommentAttribute
285+
}!
286+
287+
mut information_schema_column_comment_results := []string{}
288+
289+
for comment in column_comments {
290+
x := comment.vals[1]
291+
information_schema_column_comment_results << x or { '' }
292+
}
293+
assert information_schema_column_comment_results == ['', 'real user name', '']
294+
295+
mut information_schema_table_comment_result := []string{}
296+
for comment in table_comment {
297+
x := comment.vals[2]
298+
information_schema_table_comment_result << x or { '' }
299+
}
300+
assert information_schema_table_comment_result == ['This is a table comment']
235301
}

vlib/db/sqlite/orm.v

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,20 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
5252
// sql stmt
5353

5454
// insert is used internally by V's ORM for processing `INSERT ` queries
55-
pub fn (db DB) insert(table string, data orm.QueryData) ! {
55+
pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
5656
query, converted_data := orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?',
5757
1, data, orm.QueryData{})
5858
sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})!
5959
}
6060

6161
// update is used internally by V's ORM for processing `UPDATE ` queries
62-
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
62+
pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
6363
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data, where)
6464
sqlite_stmt_worker(db, query, data, where)!
6565
}
6666

6767
// delete is used internally by V's ORM for processing `DELETE ` queries
68-
pub fn (db DB) delete(table string, where orm.QueryData) ! {
68+
pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
6969
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{},
7070
where)
7171
sqlite_stmt_worker(db, query, orm.QueryData{}, where)!
@@ -81,16 +81,15 @@ pub fn (db DB) last_id() int {
8181
// DDL (table creation/destroying etc)
8282

8383
// create is used internally by V's ORM for processing table creation queries (DDL)
84-
pub fn (db DB) create(table string, fields []orm.TableField) ! {
85-
query := orm.orm_table_gen(table, '`', true, 0, fields, sqlite_type_from_v, false) or {
86-
return err
87-
}
84+
pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
85+
query := orm.orm_table_gen(.sqlite, table, '`', true, 0, fields, sqlite_type_from_v,
86+
false) or { return err }
8887
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
8988
}
9089

9190
// drop is used internally by V's ORM for processing table destroying queries (DDL)
92-
pub fn (db DB) drop(table string) ! {
93-
query := 'DROP TABLE `${table}`;'
91+
pub fn (db DB) drop(table orm.Table) ! {
92+
query := 'DROP TABLE `${table.name}`;'
9493
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
9594
}
9695

vlib/db/sqlite/sqlite_orm_test.v

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ fn test_sqlite_orm() {
3232
defer {
3333
db.close() or { panic(err) }
3434
}
35-
db.create('Test', [
35+
table := orm.Table{
36+
name: 'Test'
37+
}
38+
db.create(table, [
3639
orm.TableField{
3740
name: 'id'
3841
typ: typeof[int]().idx
@@ -59,13 +62,13 @@ fn test_sqlite_orm() {
5962
},
6063
]) or { panic(err) }
6164

62-
db.insert('Test', orm.QueryData{
65+
db.insert(table, orm.QueryData{
6366
fields: ['name', 'age']
6467
data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)]
6568
}) or { panic(err) }
6669

6770
res := db.select(orm.SelectConfig{
68-
table: 'Test'
71+
table: table
6972
has_where: true
7073
fields: ['id', 'name', 'age']
7174
types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx]

0 commit comments

Comments
 (0)