breaking,orm: add table attrs; add table/field comment support for mysql and pg (#24744)

This commit is contained in:
kbkpbot 2025-06-18 15:20:09 +08:00 committed by GitHub
parent d52bac1301
commit d4097212b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 376 additions and 116 deletions

View File

@ -37,6 +37,13 @@ struct TestDefaultAttribute {
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP'] created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
} }
@[comment: 'This is a table comment']
struct TestCommentAttribute {
id string @[primary; sql: serial]
name string @[comment: 'real user name']
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
}
fn test_mysql_orm() { fn test_mysql_orm() {
$if !network ? { $if !network ? {
eprintln('> Skipping test ${@FN}, since `-d network` is not passed.') eprintln('> Skipping test ${@FN}, since `-d network` is not passed.')
@ -47,13 +54,17 @@ fn test_mysql_orm() {
host: '127.0.0.1' host: '127.0.0.1'
port: 3306 port: 3306
username: 'root' username: 'root'
password: '' password: '12345678'
dbname: 'mysql' dbname: 'mysql'
)! )!
defer { defer {
db.close() db.close()
} }
db.create('Test', [ table := orm.Table{
name: 'Test'
}
db.drop(table) or {}
db.create(table, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx
@ -80,13 +91,13 @@ fn test_mysql_orm() {
}, },
]) or { panic(err) } ]) or { panic(err) }
db.insert('Test', orm.QueryData{ db.insert(table, orm.QueryData{
fields: ['name', 'age'] fields: ['name', 'age']
data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)] data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)]
}) or { panic(err) } }) or { panic(err) }
res := db.select(orm.SelectConfig{ res := db.select(orm.SelectConfig{
table: 'Test' table: table
has_where: true has_where: true
fields: ['id', 'name', 'age'] fields: ['id', 'name', 'age']
types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx] types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx]
@ -272,4 +283,49 @@ fn test_mysql_orm() {
'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP' 'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP'
}] }]
assert information_schema_column_default_sql == result_defaults.maps() assert information_schema_column_default_sql == result_defaults.maps()
/** test comment attribute
*/
sql db {
create table TestCommentAttribute
}!
mut column_comments := db.query("
SELECT COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TestCommentAttribute'
ORDER BY ORDINAL_POSITION
") or {
println(err)
panic(err)
}
mut table_comment := db.query("
SELECT TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TestCommentAttribute'
") or {
println(err)
panic(err)
}
sql db {
drop table TestCommentAttribute
}!
information_schema_column_comment_sql := [{
'COLUMN_COMMENT': ''
}, {
'COLUMN_COMMENT': 'real user name'
}, {
'COLUMN_COMMENT': ''
}]
assert information_schema_column_comment_sql == column_comments.maps()
information_schema_table_comment_sql := [
{
'TABLE_COMMENT': 'This is a table comment'
},
]
assert information_schema_table_comment_sql == table_comment.maps()
} }

View File

@ -122,8 +122,8 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
} }
// insert is used internally by V's ORM for processing `INSERT ` queries // insert is used internally by V's ORM for processing `INSERT ` queries
pub fn (db DB) insert(table string, data orm.QueryData) ! { pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
mut converted_primitive_array := db.convert_query_data_to_primitives(table, data)! mut converted_primitive_array := db.convert_query_data_to_primitives(table.name, data)!
converted_primitive_data := orm.QueryData{ converted_primitive_data := orm.QueryData{
fields: data.fields fields: data.fields
@ -139,13 +139,13 @@ pub fn (db DB) insert(table string, data orm.QueryData) ! {
} }
// update is used internally by V's ORM for processing `UPDATE ` queries // update is used internally by V's ORM for processing `UPDATE ` queries
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! { pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.default, table, '`', .update, false, '?', 1, data, where) query, _ := orm.orm_stmt_gen(.default, table, '`', .update, false, '?', 1, data, where)
mysql_stmt_worker(db, query, data, where)! mysql_stmt_worker(db, query, data, where)!
} }
// delete is used internally by V's ORM for processing `DELETE ` queries // delete is used internally by V's ORM for processing `DELETE ` queries
pub fn (db DB) delete(table string, where orm.QueryData) ! { pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.default, table, '`', .delete, false, '?', 1, orm.QueryData{}, query, _ := orm.orm_stmt_gen(.default, table, '`', .delete, false, '?', 1, orm.QueryData{},
where) where)
mysql_stmt_worker(db, query, orm.QueryData{}, where)! mysql_stmt_worker(db, query, orm.QueryData{}, where)!
@ -160,16 +160,15 @@ pub fn (db DB) last_id() int {
} }
// create is used internally by V's ORM for processing table creation queries (DDL) // create is used internally by V's ORM for processing table creation queries (DDL)
pub fn (db DB) create(table string, fields []orm.TableField) ! { pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
query := orm.orm_table_gen(table, '`', true, 0, fields, mysql_type_from_v, false) or { query := orm.orm_table_gen(.mysql, table, '`', true, 0, fields, mysql_type_from_v,
return err false) or { return err }
}
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
} }
// drop is used internally by V's ORM for processing table destroying queries (DDL) // drop is used internally by V's ORM for processing table destroying queries (DDL)
pub fn (db DB) drop(table string) ! { pub fn (db DB) drop(table orm.Table) ! {
query := 'DROP TABLE `${table}`;' query := 'DROP TABLE `${table.name}`;'
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
} }

View File

@ -31,20 +31,20 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
// sql stmt // sql stmt
// insert is used internally by V's ORM for processing `INSERT ` queries // insert is used internally by V's ORM for processing `INSERT ` queries
pub fn (db DB) insert(table string, data orm.QueryData) ! { pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
query, converted_data := orm.orm_stmt_gen(.default, table, '"', .insert, true, '$', query, converted_data := orm.orm_stmt_gen(.default, table, '"', .insert, true, '$',
1, data, orm.QueryData{}) 1, data, orm.QueryData{})
pg_stmt_worker(db, query, converted_data, orm.QueryData{})! pg_stmt_worker(db, query, converted_data, orm.QueryData{})!
} }
// update is used internally by V's ORM for processing `UPDATE ` queries // update is used internally by V's ORM for processing `UPDATE ` queries
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! { pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data, where) query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data, where)
pg_stmt_worker(db, query, data, where)! pg_stmt_worker(db, query, data, where)!
} }
// delete is used internally by V's ORM for processing `DELETE ` queries // delete is used internally by V's ORM for processing `DELETE ` queries
pub fn (db DB) delete(table string, where orm.QueryData) ! { pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{}, query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{},
where) where)
pg_stmt_worker(db, query, orm.QueryData{}, where)! pg_stmt_worker(db, query, orm.QueryData{}, where)!
@ -60,14 +60,21 @@ pub fn (db DB) last_id() int {
// DDL (table creation/destroying etc) // DDL (table creation/destroying etc)
// create is used internally by V's ORM for processing table creation queries (DDL) // create is used internally by V's ORM for processing table creation queries (DDL)
pub fn (db DB) create(table string, fields []orm.TableField) ! { pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
query := orm.orm_table_gen(table, '"', true, 0, fields, pg_type_from_v, false) or { return err } query := orm.orm_table_gen(.pg, table, '"', true, 0, fields, pg_type_from_v, false) or {
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! return err
}
stmts := query.split(';')
for stmt in stmts {
if stmt != '' {
pg_stmt_worker(db, stmt + ';', orm.QueryData{}, orm.QueryData{})!
}
}
} }
// drop is used internally by V's ORM for processing table destroying queries (DDL) // drop is used internally by V's ORM for processing table destroying queries (DDL)
pub fn (db DB) drop(table string) ! { pub fn (db DB) drop(table orm.Table) ! {
query := 'DROP TABLE "${table}";' query := 'DROP TABLE "${table.name}";'
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
} }

View File

@ -36,6 +36,13 @@ struct TestDefaultAttribute {
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP'] created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
} }
@[comment: 'This is a table comment']
struct TestCommentAttribute {
id string @[primary; sql: serial]
name string @[comment: 'real user name']
created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
}
fn test_pg_orm() { fn test_pg_orm() {
$if !network ? { $if !network ? {
eprintln('> Skipping test ${@FN}, since `-d network` is not passed.') eprintln('> Skipping test ${@FN}, since `-d network` is not passed.')
@ -46,15 +53,18 @@ fn test_pg_orm() {
host: 'localhost' host: 'localhost'
user: 'postgres' user: 'postgres'
password: '12345678' password: '12345678'
dbname: 'test' dbname: 'postgres'
) or { panic(err) } ) or { panic(err) }
defer { defer {
db.close() db.close()
} }
db.drop('Test')! table := orm.Table{
name: 'Test'
}
db.drop(table) or {}
db.create('Test', [ db.create(table, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[string]().idx typ: typeof[string]().idx
@ -94,13 +104,13 @@ fn test_pg_orm() {
}, },
]) or { panic(err) } ]) or { panic(err) }
db.insert('Test', orm.QueryData{ db.insert(table, orm.QueryData{
fields: ['name', 'age'] fields: ['name', 'age']
data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)] data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)]
}) or { panic(err) } }) or { panic(err) }
res := db.select(orm.SelectConfig{ res := db.select(orm.SelectConfig{
table: 'Test' table: table
is_count: false is_count: false
has_where: true has_where: true
has_order: false has_order: false
@ -143,7 +153,7 @@ fn test_pg_orm() {
*/ */
sql db { sql db {
drop table TestCustomSqlType drop table TestCustomSqlType
}! } or {}
sql db { sql db {
create table TestCustomSqlType create table TestCustomSqlType
@ -232,4 +242,60 @@ fn test_pg_orm() {
drop table TestDefaultAttribute drop table TestDefaultAttribute
}! }!
assert ['gen_random_uuid()', '', 'CURRENT_TIMESTAMP'] == information_schema_defaults_results assert ['gen_random_uuid()', '', 'CURRENT_TIMESTAMP'] == information_schema_defaults_results
/** test comment attribute
*/
sql db {
create table TestCommentAttribute
}!
mut column_comments := db.exec("
SELECT
a.attname AS column_name,
col_description(a.attrelid, a.attnum) AS column_comment
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'TestCommentAttribute'
AND n.nspname = 'public'
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
") or {
println(err)
panic(err)
}
mut table_comment := db.exec("
SELECT
nspname AS schema_name,
relname AS table_name,
obj_description(pc.oid) AS table_comment
FROM pg_class pc
JOIN pg_namespace pn ON pn.oid = pc.relnamespace
WHERE pc.relkind = 'r' AND pc.relname = 'TestCommentAttribute'
ORDER BY schema_name, table_name
") or {
println(err)
panic(err)
}
sql db {
drop table TestCommentAttribute
}!
mut information_schema_column_comment_results := []string{}
for comment in column_comments {
x := comment.vals[1]
information_schema_column_comment_results << x or { '' }
}
assert information_schema_column_comment_results == ['', 'real user name', '']
mut information_schema_table_comment_result := []string{}
for comment in table_comment {
x := comment.vals[2]
information_schema_table_comment_result << x or { '' }
}
assert information_schema_table_comment_result == ['This is a table comment']
} }

View File

@ -52,20 +52,20 @@ pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
// sql stmt // sql stmt
// insert is used internally by V's ORM for processing `INSERT ` queries // insert is used internally by V's ORM for processing `INSERT ` queries
pub fn (db DB) insert(table string, data orm.QueryData) ! { pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
query, converted_data := orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?', query, converted_data := orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?',
1, data, orm.QueryData{}) 1, data, orm.QueryData{})
sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})! sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})!
} }
// update is used internally by V's ORM for processing `UPDATE ` queries // update is used internally by V's ORM for processing `UPDATE ` queries
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! { pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data, where) query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data, where)
sqlite_stmt_worker(db, query, data, where)! sqlite_stmt_worker(db, query, data, where)!
} }
// delete is used internally by V's ORM for processing `DELETE ` queries // delete is used internally by V's ORM for processing `DELETE ` queries
pub fn (db DB) delete(table string, where orm.QueryData) ! { pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{}, query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{},
where) where)
sqlite_stmt_worker(db, query, orm.QueryData{}, where)! sqlite_stmt_worker(db, query, orm.QueryData{}, where)!
@ -81,16 +81,15 @@ pub fn (db DB) last_id() int {
// DDL (table creation/destroying etc) // DDL (table creation/destroying etc)
// create is used internally by V's ORM for processing table creation queries (DDL) // create is used internally by V's ORM for processing table creation queries (DDL)
pub fn (db DB) create(table string, fields []orm.TableField) ! { pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
query := orm.orm_table_gen(table, '`', true, 0, fields, sqlite_type_from_v, false) or { query := orm.orm_table_gen(.sqlite, table, '`', true, 0, fields, sqlite_type_from_v,
return err false) or { return err }
}
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
} }
// drop is used internally by V's ORM for processing table destroying queries (DDL) // drop is used internally by V's ORM for processing table destroying queries (DDL)
pub fn (db DB) drop(table string) ! { pub fn (db DB) drop(table orm.Table) ! {
query := 'DROP TABLE `${table}`;' query := 'DROP TABLE `${table.name}`;'
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})! sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
} }

View File

@ -32,7 +32,10 @@ fn test_sqlite_orm() {
defer { defer {
db.close() or { panic(err) } db.close() or { panic(err) }
} }
db.create('Test', [ table := orm.Table{
name: 'Test'
}
db.create(table, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx
@ -59,13 +62,13 @@ fn test_sqlite_orm() {
}, },
]) or { panic(err) } ]) or { panic(err) }
db.insert('Test', orm.QueryData{ db.insert(table, orm.QueryData{
fields: ['name', 'age'] fields: ['name', 'age']
data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)] data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)]
}) or { panic(err) } }) or { panic(err) }
res := db.select(orm.SelectConfig{ res := db.select(orm.SelectConfig{
table: 'Test' table: table
has_where: true has_where: true
fields: ['id', 'name', 'age'] fields: ['id', 'name', 'age']
types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx] types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx]

View File

@ -91,6 +91,8 @@ pub enum OrderType {
pub enum SQLDialect { pub enum SQLDialect {
default default
mysql
pg
sqlite sqlite
} }
@ -153,6 +155,12 @@ pub:
right Primitive right Primitive
} }
pub struct Table {
pub mut:
name string
attrs []VAttribute
}
pub struct TableField { pub struct TableField {
pub mut: pub mut:
name string name string
@ -163,7 +171,7 @@ pub mut:
is_arr bool is_arr bool
} }
// table - Table name // table - Table struct
// is_count - Either the data will be returned or an integer with the count // is_count - Either the data will be returned or an integer with the count
// has_where - Select all or use a where expr // has_where - Select all or use a where expr
// has_order - Order the results // has_order - Order the results
@ -176,7 +184,7 @@ pub mut:
// types - Types to select // types - Types to select
pub struct SelectConfig { pub struct SelectConfig {
pub mut: pub mut:
table string table Table
is_count bool is_count bool
has_where bool has_where bool
has_order bool has_order bool
@ -200,11 +208,11 @@ pub mut:
pub interface Connection { pub interface Connection {
mut: mut:
select(config SelectConfig, data QueryData, where QueryData) ![][]Primitive select(config SelectConfig, data QueryData, where QueryData) ![][]Primitive
insert(table string, data QueryData) ! insert(table Table, data QueryData) !
update(table string, data QueryData, where QueryData) ! update(table Table, data QueryData, where QueryData) !
delete(table string, where QueryData) ! delete(table Table, where QueryData) !
create(table string, fields []TableField) ! create(table Table, fields []TableField) !
drop(table string) ! drop(table Table) !
last_id() int last_id() int
} }
@ -213,7 +221,7 @@ mut:
// num - Stmt uses nums at prepared statements (? or ?1) // num - Stmt uses nums at prepared statements (? or ?1)
// qm - Character for prepared statement (qm for question mark, as in sqlite) // qm - Character for prepared statement (qm for question mark, as in sqlite)
// start_pos - When num is true, it's the start position of the counter // start_pos - When num is true, it's the start position of the counter
pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKind, num bool, qm string, pub fn orm_stmt_gen(sql_dialect SQLDialect, table Table, q string, kind StmtKind, num bool, qm string,
start_pos int, data QueryData, where QueryData) (string, QueryData) { start_pos int, data QueryData, where QueryData) (string, QueryData) {
mut str := '' mut str := ''
mut c := start_pos mut c := start_pos
@ -257,7 +265,7 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
c++ c++
} }
str += 'INSERT INTO ${q}${table}${q} ' str += 'INSERT INTO ${q}${table.name}${q} '
are_values_empty := values.len == 0 are_values_empty := values.len == 0
@ -272,7 +280,7 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
} }
} }
.update { .update {
str += 'UPDATE ${q}${table}${q} SET ' str += 'UPDATE ${q}${table.name}${q} SET '
for i, field in data.fields { for i, field in data.fields {
str += '${q}${field}${q} = ' str += '${q}${field}${q} = '
if data.data.len > i { if data.data.len > i {
@ -310,7 +318,7 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
str += ' WHERE ' str += ' WHERE '
} }
.delete { .delete {
str += 'DELETE FROM ${q}${table}${q} WHERE ' str += 'DELETE FROM ${q}${table.name}${q} WHERE '
} }
} }
// where // where
@ -319,7 +327,7 @@ pub fn orm_stmt_gen(sql_dialect SQLDialect, table string, q string, kind StmtKin
} }
str += ';' str += ';'
$if trace_orm_stmt ? { $if trace_orm_stmt ? {
eprintln('> orm_stmt sql_dialect: ${sql_dialect} | table: ${table} | kind: ${kind} | query: ${str}') eprintln('> orm_stmt sql_dialect: ${sql_dialect} | table: ${table.name} | kind: ${kind} | query: ${str}')
} }
$if trace_orm ? { $if trace_orm ? {
eprintln('> orm: ${str}') eprintln('> orm: ${str}')
@ -352,7 +360,7 @@ pub fn orm_select_gen(cfg SelectConfig, q string, num bool, qm string, start_pos
} }
} }
str += ' FROM ${q}${cfg.table}${q}' str += ' FROM ${q}${cfg.table.name}${q}'
mut c := start_pos mut c := start_pos
@ -441,19 +449,19 @@ fn gen_where_clause(where QueryData, q string, qm string, num bool, mut c &int)
} }
// Generates an sql table stmt, from universal parameter // Generates an sql table stmt, from universal parameter
// table - Table name // table - Table struct
// q - see orm_stmt_gen // q - see orm_stmt_gen
// defaults - enables default values in stmt // defaults - enables default values in stmt
// def_unique_len - sets default unique length for texts // def_unique_len - sets default unique length for texts
// fields - See TableField // fields - See TableField
// sql_from_v - Function which maps type indices to sql type names // sql_from_v - Function which maps type indices to sql type names
// alternative - Needed for msdb // alternative - Needed for msdb
pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, fields []TableField, sql_from_v fn (int) !string, pub fn orm_table_gen(sql_dialect SQLDialect, table Table, q string, defaults bool, def_unique_len int, fields []TableField, sql_from_v fn (int) !string,
alternative bool) !string { alternative bool) !string {
mut str := 'CREATE TABLE IF NOT EXISTS ${q}${table}${q} (' mut str := 'CREATE TABLE IF NOT EXISTS ${q}${table.name}${q} ('
if alternative { if alternative {
str = 'IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${q}${table}${q} and xtype=${q}U${q}) CREATE TABLE ${q}${table}${q} (' str = 'IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${q}${table.name}${q} and xtype=${q}U${q}) CREATE TABLE ${q}${table.name}${q} ('
} }
mut fs := []string{} mut fs := []string{}
@ -461,6 +469,19 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
mut unique := map[string][]string{} mut unique := map[string][]string{}
mut primary := '' mut primary := ''
mut primary_typ := 0 mut primary_typ := 0
mut table_comment := ''
mut field_comments := map[string]string{}
for attr in table.attrs {
match attr.name {
'comment' {
if attr.arg != '' && attr.kind == .string {
table_comment = attr.arg.replace('"', '\\"')
}
}
else {}
}
}
for field in fields { for field in fields {
if field.is_arr { if field.is_arr {
@ -473,6 +494,7 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
mut unique_len := 0 mut unique_len := 0
mut references_table := '' mut references_table := ''
mut references_field := '' mut references_field := ''
mut field_comment := ''
mut field_name := sql_field_name(field) mut field_name := sql_field_name(field)
mut col_typ := sql_from_v(sql_field_type(field)) or { mut col_typ := sql_from_v(sql_field_type(field)) or {
field_name = '${field_name}_id' field_name = '${field_name}_id'
@ -540,6 +562,12 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
} }
} }
} }
'comment' {
if attr.arg != '' && attr.kind == .string {
field_comment = attr.arg.replace("'", "\\'")
field_comments[field_name] = field_comment
}
}
else {} else {}
} }
} }
@ -548,12 +576,15 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
} }
mut stmt := '' mut stmt := ''
if col_typ == '' { if col_typ == '' {
return error('Unknown type (${field.typ}) for field ${field.name} in struct ${table}') return error('Unknown type (${field.typ}) for field ${field.name} in struct ${table.name}')
} }
stmt = '${q}${field_name}${q} ${col_typ}' stmt = '${q}${field_name}${q} ${col_typ}'
if defaults && default_val != '' { if defaults && default_val != '' {
stmt += ' DEFAULT ${default_val}' stmt += ' DEFAULT ${default_val}'
} }
if sql_dialect == .mysql && field_comment != '' {
stmt += " COMMENT '${field_comment}'"
}
if !nullable { if !nullable {
stmt += ' NOT NULL' stmt += ' NOT NULL'
} }
@ -591,9 +622,22 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
fs << unique_fields fs << unique_fields
str += fs.join(', ') str += fs.join(', ')
str += ');' str += ')'
if sql_dialect == .mysql && table_comment != '' {
str += " COMMENT = '${table_comment}'"
}
str += ';'
if sql_dialect == .pg {
if table_comment != '' {
str += "\nCOMMENT ON TABLE \"${table.name}\" IS '${table_comment}';"
}
for f, c in field_comments {
str += "\nCOMMENT ON COLUMN \"${table.name}\".\"${f}\" IS '${c}';"
}
}
$if trace_orm_create ? { $if trace_orm_create ? {
eprintln('> orm_create table: ${table} | query: ${str}') eprintln('> orm_create table: ${table.name} | query: ${str}')
} }
$if trace_orm ? { $if trace_orm ? {
eprintln('> orm: ${str}') eprintln('> orm: ${str}')

View File

@ -3,7 +3,10 @@
import orm import orm
fn test_orm_stmt_gen_update() { fn test_orm_stmt_gen_update() {
query_and, _ := orm.orm_stmt_gen(.default, 'Test', "'", .update, true, '?', 0, orm.QueryData{ table := orm.Table{
name: 'Test'
}
query_and, _ := orm.orm_stmt_gen(.default, table, "'", .update, true, '?', 0, orm.QueryData{
fields: ['test', 'a'] fields: ['test', 'a']
data: [] data: []
types: [] types: []
@ -17,7 +20,7 @@ fn test_orm_stmt_gen_update() {
}) })
assert query_and == "UPDATE 'Test' SET 'test' = ?0, 'a' = ?1 WHERE 'id' >= ?2 AND 'name' = ?3;" assert query_and == "UPDATE 'Test' SET 'test' = ?0, 'a' = ?1 WHERE 'id' >= ?2 AND 'name' = ?3;"
query_or, _ := orm.orm_stmt_gen(.default, 'Test', "'", .update, true, '?', 0, orm.QueryData{ query_or, _ := orm.orm_stmt_gen(.default, table, "'", .update, true, '?', 0, orm.QueryData{
fields: ['test', 'a'] fields: ['test', 'a']
data: [] data: []
types: [] types: []
@ -33,7 +36,10 @@ fn test_orm_stmt_gen_update() {
} }
fn test_orm_stmt_gen_insert() { fn test_orm_stmt_gen_insert() {
query, _ := orm.orm_stmt_gen(.default, 'Test', "'", .insert, true, '?', 0, orm.QueryData{ table := orm.Table{
name: 'Test'
}
query, _ := orm.orm_stmt_gen(.default, table, "'", .insert, true, '?', 0, orm.QueryData{
fields: ['test', 'a'] fields: ['test', 'a']
data: [] data: []
types: [] types: []
@ -43,7 +49,10 @@ fn test_orm_stmt_gen_insert() {
} }
fn test_orm_stmt_gen_delete() { fn test_orm_stmt_gen_delete() {
query_and, _ := orm.orm_stmt_gen(.default, 'Test', "'", .delete, true, '?', 0, orm.QueryData{ table := orm.Table{
name: 'Test'
}
query_and, _ := orm.orm_stmt_gen(.default, table, "'", .delete, true, '?', 0, orm.QueryData{
fields: ['test', 'a'] fields: ['test', 'a']
data: [] data: []
types: [] types: []
@ -57,7 +66,7 @@ fn test_orm_stmt_gen_delete() {
}) })
assert query_and == "DELETE FROM 'Test' WHERE 'id' >= ?0 AND 'name' = ?1;" assert query_and == "DELETE FROM 'Test' WHERE 'id' >= ?0 AND 'name' = ?1;"
query_or, _ := orm.orm_stmt_gen(.default, 'Test', "'", .delete, true, '?', 0, orm.QueryData{ query_or, _ := orm.orm_stmt_gen(.default, table, "'", .delete, true, '?', 0, orm.QueryData{
fields: ['test', 'a'] fields: ['test', 'a']
data: [] data: []
types: [] types: []
@ -78,7 +87,9 @@ fn get_select_fields() []string {
fn test_orm_select_gen() { fn test_orm_select_gen() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
}, "'", true, '?', 0, orm.QueryData{}) }, "'", true, '?', 0, orm.QueryData{})
@ -87,7 +98,9 @@ fn test_orm_select_gen() {
fn test_orm_select_gen_with_limit() { fn test_orm_select_gen_with_limit() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
has_limit: true has_limit: true
}, "'", true, '?', 0, orm.QueryData{}) }, "'", true, '?', 0, orm.QueryData{})
@ -97,7 +110,9 @@ fn test_orm_select_gen_with_limit() {
fn test_orm_select_gen_with_where() { fn test_orm_select_gen_with_where() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
has_where: true has_where: true
}, "'", true, '?', 0, orm.QueryData{ }, "'", true, '?', 0, orm.QueryData{
@ -111,7 +126,9 @@ fn test_orm_select_gen_with_where() {
fn test_orm_select_gen_with_order() { fn test_orm_select_gen_with_order() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
has_order: true has_order: true
order_type: .desc order_type: .desc
@ -122,7 +139,9 @@ fn test_orm_select_gen_with_order() {
fn test_orm_select_gen_with_offset() { fn test_orm_select_gen_with_offset() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
has_offset: true has_offset: true
}, "'", true, '?', 0, orm.QueryData{}) }, "'", true, '?', 0, orm.QueryData{})
@ -132,7 +151,9 @@ fn test_orm_select_gen_with_offset() {
fn test_orm_select_gen_with_all() { fn test_orm_select_gen_with_all() {
query := orm.orm_select_gen(orm.SelectConfig{ query := orm.orm_select_gen(orm.SelectConfig{
table: 'test_table' table: orm.Table{
name: 'test_table'
}
fields: get_select_fields() fields: get_select_fields()
has_limit: true has_limit: true
has_order: true has_order: true
@ -149,7 +170,10 @@ fn test_orm_select_gen_with_all() {
} }
fn test_orm_table_gen() { fn test_orm_table_gen() {
query := orm.orm_table_gen('test_table', "'", true, 0, [ table := orm.Table{
name: 'test_table'
}
query := orm.orm_table_gen(.default, table, "'", true, 0, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx
@ -181,7 +205,7 @@ fn test_orm_table_gen() {
], sql_type_from_v, false) or { panic(err) } ], sql_type_from_v, false) or { panic(err) }
assert query == "CREATE TABLE IF NOT EXISTS 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));" assert query == "CREATE TABLE IF NOT EXISTS 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));"
alt_query := orm.orm_table_gen('test_table', "'", true, 0, [ alt_query := orm.orm_table_gen(.default, table, "'", true, 0, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx
@ -213,7 +237,7 @@ fn test_orm_table_gen() {
], sql_type_from_v, true) or { panic(err) } ], sql_type_from_v, true) or { panic(err) }
assert alt_query == "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='test_table' and xtype='U') CREATE TABLE 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));" assert alt_query == "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='test_table' and xtype='U') CREATE TABLE 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));"
unique_query := orm.orm_table_gen('test_table', "'", true, 0, [ unique_query := orm.orm_table_gen(.default, table, "'", true, 0, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx
@ -248,7 +272,7 @@ fn test_orm_table_gen() {
], sql_type_from_v, false) or { panic(err) } ], sql_type_from_v, false) or { panic(err) }
assert unique_query == "CREATE TABLE IF NOT EXISTS 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT NOT NULL, 'abc' INT64 DEFAULT 6754 NOT NULL, PRIMARY KEY('id'), UNIQUE('test'));" assert unique_query == "CREATE TABLE IF NOT EXISTS 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT NOT NULL, 'abc' INT64 DEFAULT 6754 NOT NULL, PRIMARY KEY('id'), UNIQUE('test'));"
mult_unique_query := orm.orm_table_gen('test_table', "'", true, 0, [ mult_unique_query := orm.orm_table_gen(.default, table, "'", true, 0, [
orm.TableField{ orm.TableField{
name: 'id' name: 'id'
typ: typeof[int]().idx typ: typeof[int]().idx

View File

@ -25,7 +25,7 @@ pub fn new_query[T](conn Connection) &QueryBuilder[T] {
valid_sql_field_names: meta.map(sql_field_name(it)) valid_sql_field_names: meta.map(sql_field_name(it))
conn: conn conn: conn
config: SelectConfig{ config: SelectConfig{
table: table_name_from_struct[T]() table: table_from_struct[T]()
} }
data: QueryData{} data: QueryData{}
where: QueryData{} where: QueryData{}
@ -35,9 +35,9 @@ pub fn new_query[T](conn Connection) &QueryBuilder[T] {
// reset reset a query object, but keep the connection and table name // reset reset a query object, but keep the connection and table name
pub fn (qb_ &QueryBuilder[T]) reset() &QueryBuilder[T] { pub fn (qb_ &QueryBuilder[T]) reset() &QueryBuilder[T] {
mut qb := unsafe { qb_ } mut qb := unsafe { qb_ }
old_table_name := qb.config.table old_table := qb.config.table
qb.config = SelectConfig{ qb.config = SelectConfig{
table: old_table_name table: old_table
} }
qb.data = QueryData{} qb.data = QueryData{}
qb.where = QueryData{} qb.where = QueryData{}
@ -366,15 +366,20 @@ pub fn (qb_ &QueryBuilder[T]) set(assign string, values ...Primitive) !&QueryBui
return qb return qb
} }
// table_name_from_struct get table name from struct // table_from_struct get table from struct
fn table_name_from_struct[T]() string { fn table_from_struct[T]() Table {
mut table_name := T.name mut table_name := T.name
mut attrs := []VAttribute{}
$for a in T.attributes { $for a in T.attributes {
$if a.name == 'table' && a.has_arg { $if a.name == 'table' && a.has_arg {
table_name = a.arg table_name = a.arg
} }
attrs << a
}
return Table{
name: table_name
attrs: attrs
} }
return table_name
} }
// struct_meta return a struct's fields info // struct_meta return a struct's fields info

View File

@ -25,14 +25,14 @@ fn (mut db Database) select(config orm.SelectConfig, data orm.QueryData, where o
} }
// insert is used internally by V's ORM for processing `INSERT` queries // insert is used internally by V's ORM for processing `INSERT` queries
fn (mut db Database) insert(table string, data orm.QueryData) ! { fn (mut db Database) insert(table orm.Table, data orm.QueryData) ! {
query, _ := orm.orm_stmt_gen(.sqlite, table, '', .insert, false, '?', 1, data, orm.QueryData{}) query, _ := orm.orm_stmt_gen(.sqlite, table, '', .insert, false, '?', 1, data, orm.QueryData{})
db.query(query)! db.query(query)!
} }
// update is used internally by V's ORM for processing `UPDATE` queries // update is used internally by V's ORM for processing `UPDATE` queries
fn (mut db Database) update(table string, data orm.QueryData, where orm.QueryData) ! { fn (mut db Database) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .update, true, ':', 1, data, mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .update, true, ':', 1, data,
where) where)
@ -40,7 +40,7 @@ fn (mut db Database) update(table string, data orm.QueryData, where orm.QueryDat
} }
// delete is used internally by V's ORM for processing `DELETE ` queries // delete is used internally by V's ORM for processing `DELETE ` queries
fn (mut db Database) delete(table string, where orm.QueryData) ! { fn (mut db Database) delete(table orm.Table, where orm.QueryData) ! {
query, converted := orm.orm_stmt_gen(.sqlite, table, '', .delete, true, ':', 1, orm.QueryData{}, query, converted := orm.orm_stmt_gen(.sqlite, table, '', .delete, true, ':', 1, orm.QueryData{},
where) where)
@ -66,16 +66,15 @@ fn sqlite_type_from_v(typ int) !string {
} }
// create is used internally by V's ORM for processing table creation queries (DDL) // create is used internally by V's ORM for processing table creation queries (DDL)
fn (mut db Database) create(table string, fields []orm.TableField) ! { fn (mut db Database) create(table orm.Table, fields []orm.TableField) ! {
mut query := orm.orm_table_gen(table, '', true, 0, fields, sqlite_type_from_v, false) or { mut query := orm.orm_table_gen(.sqlite, table, '', true, 0, fields, sqlite_type_from_v,
return err false) or { return err }
}
db.query(query)! db.query(query)!
} }
// drop is used internally by V's ORM for processing table destroying queries (DDL) // drop is used internally by V's ORM for processing table destroying queries (DDL)
fn (mut db Database) drop(table string) ! { fn (mut db Database) drop(table orm.Table) ! {
query := 'DROP TABLE ${table};' query := 'DROP TABLE ${table.name};'
$if trace_orm ? { $if trace_orm ? {
eprintln('> vsql drop: ${query}') eprintln('> vsql drop: ${query}')
} }

View File

@ -31,7 +31,7 @@ fn (db MockDB) select(config orm.SelectConfig, data orm.QueryData, where orm.Que
return db.db.select(config, data, where) return db.db.select(config, data, where)
} }
fn (db MockDB) insert(table string, data orm.QueryData) ! { fn (db MockDB) insert(table orm.Table, data orm.QueryData) ! {
mut st := db.st mut st := db.st
last, qdata := orm.orm_stmt_gen(.sqlite, table, '`', .insert, false, '?', 1, data, last, qdata := orm.orm_stmt_gen(.sqlite, table, '`', .insert, false, '?', 1, data,
orm.QueryData{}) orm.QueryData{})
@ -41,7 +41,7 @@ fn (db MockDB) insert(table string, data orm.QueryData) ! {
return db.db.insert(table, data) return db.db.insert(table, data)
} }
fn (db MockDB) update(table string, data orm.QueryData, where orm.QueryData) ! { fn (db MockDB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
mut st := db.st mut st := db.st
st.last, _ = orm.orm_stmt_gen(.sqlite, table, '`', .update, false, '?', 1, data, where) st.last, _ = orm.orm_stmt_gen(.sqlite, table, '`', .update, false, '?', 1, data, where)
st.data = data.data st.data = data.data
@ -49,7 +49,7 @@ fn (db MockDB) update(table string, data orm.QueryData, where orm.QueryData) ! {
return db.db.update(table, data, where) return db.db.update(table, data, where)
} }
fn (db MockDB) delete(table string, where orm.QueryData) ! { fn (db MockDB) delete(table orm.Table, where orm.QueryData) ! {
mut st := db.st mut st := db.st
st.last, _ = orm.orm_stmt_gen(.sqlite, table, '`', .delete, false, '?', 1, orm.QueryData{}, st.last, _ = orm.orm_stmt_gen(.sqlite, table, '`', .delete, false, '?', 1, orm.QueryData{},
where) where)
@ -82,13 +82,14 @@ fn mock_type_from_v(typ int) !string {
} }
} }
fn (db MockDB) create(table string, fields []orm.TableField) ! { fn (db MockDB) create(table orm.Table, fields []orm.TableField) ! {
mut st := db.st mut st := db.st
st.last = orm.orm_table_gen(table, '`', true, 0, fields, mock_type_from_v, false)! st.last = orm.orm_table_gen(.sqlite, table, '`', true, 0, fields, mock_type_from_v,
false)!
return db.db.create(table, fields) return db.db.create(table, fields)
} }
fn (db MockDB) drop(table string) ! { fn (db MockDB) drop(table orm.Table) ! {
return db.db.drop(table) return db.db.drop(table)
} }

View File

@ -48,6 +48,7 @@ fn (mut g Gen) sql_insert_expr(node ast.SqlExpr) {
connection_var_name := g.new_tmp_var() connection_var_name := g.new_tmp_var()
g.write_orm_connection_init(connection_var_name, &node.db_expr) g.write_orm_connection_init(connection_var_name, &node.db_expr)
table_name := g.get_table_name_by_struct_type(node.table_expr.typ) table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
table_attrs := g.get_table_attrs_by_struct_type(node.table_expr.typ)
result_var_name := g.new_tmp_var() result_var_name := g.new_tmp_var()
g.sql_table_name = g.table.sym(node.table_expr.typ).name g.sql_table_name = g.table.sym(node.table_expr.typ).name
@ -55,10 +56,11 @@ fn (mut g Gen) sql_insert_expr(node ast.SqlExpr) {
hack_stmt_line := ast.SqlStmtLine{ hack_stmt_line := ast.SqlStmtLine{
object_var: node.inserted_var object_var: node.inserted_var
fields: node.fields fields: node.fields
table_expr: node.table_expr
// sub_structs: node.sub_structs // sub_structs: node.sub_structs
} }
g.write_orm_insert(hack_stmt_line, table_name, connection_var_name, result_var_name, g.write_orm_insert(hack_stmt_line, table_name, connection_var_name, result_var_name,
node.or_expr) node.or_expr, table_attrs)
g.write2(left, 'orm__Connection_name_table[${connection_var_name}._typ]._method_last_id(${connection_var_name}._object)') g.write2(left, 'orm__Connection_name_table[${connection_var_name}._typ]._method_last_id(${connection_var_name}._object)')
} }
@ -93,6 +95,7 @@ fn (mut g Gen) sql_stmt_line(stmt_line ast.SqlStmtLine, connection_var_name stri
g.sql_last_stmt_out_len = g.out.len g.sql_last_stmt_out_len = g.out.len
mut node := stmt_line mut node := stmt_line
table_name := g.get_table_name_by_struct_type(node.table_expr.typ) table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
table_attrs := g.get_table_attrs_by_struct_type(node.table_expr.typ)
result_var_name := g.new_tmp_var() result_var_name := g.new_tmp_var()
g.sql_table_name = g.table.sym(node.table_expr.typ).name g.sql_table_name = g.table.sym(node.table_expr.typ).name
@ -101,15 +104,18 @@ fn (mut g Gen) sql_stmt_line(stmt_line ast.SqlStmtLine, connection_var_name stri
} }
if node.kind == .create { if node.kind == .create {
g.write_orm_create_table(node, table_name, connection_var_name, result_var_name) g.write_orm_create_table(node, table_name, connection_var_name, result_var_name,
table_attrs)
} else if node.kind == .drop { } else if node.kind == .drop {
g.write_orm_drop_table(table_name, connection_var_name, result_var_name) g.write_orm_drop_table(node, table_name, connection_var_name, result_var_name,
table_attrs)
} else if node.kind == .insert { } else if node.kind == .insert {
g.write_orm_insert(node, table_name, connection_var_name, result_var_name, or_expr) g.write_orm_insert(node, table_name, connection_var_name, result_var_name, or_expr,
table_attrs)
} else if node.kind == .update { } else if node.kind == .update {
g.write_orm_update(node, table_name, connection_var_name, result_var_name) g.write_orm_update(node, table_name, connection_var_name, result_var_name, table_attrs)
} else if node.kind == .delete { } else if node.kind == .delete {
g.write_orm_delete(node, table_name, connection_var_name, result_var_name) g.write_orm_delete(node, table_name, connection_var_name, result_var_name, table_attrs)
} }
g.or_block(result_var_name, or_expr, ast.int_type.set_flag(.result)) g.or_block(result_var_name, or_expr, ast.int_type.set_flag(.result))
@ -138,14 +144,54 @@ fn (mut g Gen) write_orm_connection_init(connection_var_name string, db_expr &as
} }
} }
// write_orm_table_struct writes C code for the orm.Table struct
fn (mut g Gen) write_orm_table_struct(typ ast.Type) {
table_name := g.get_table_name_by_struct_type(typ)
table_attrs := g.get_table_attrs_by_struct_type(typ)
g.writeln('((orm__Table){')
g.indent++
g.writeln('.name = _S("${table_name}"),')
g.writeln('.attrs = new_array_from_c_array(${table_attrs.len}, ${table_attrs.len}, sizeof(VAttribute),')
g.indent++
if table_attrs.len > 0 {
g.write('_MOV((VAttribute[${table_attrs.len}]){')
g.indent++
for attr in table_attrs {
g.write('(VAttribute){')
g.indent++
name1 := util.smart_quote(attr.name, false)
name := cescape_nonascii(name1)
g.write(' .name = _S("${name}"),')
g.write(' .has_arg = ${attr.has_arg},')
arg1 := util.smart_quote(attr.arg, false)
arg := cescape_nonascii(arg1)
g.write(' .arg = _S("${arg}"),')
g.write(' .kind = ${int(attr.kind)},')
g.indent--
g.write('},')
}
g.indent--
g.writeln('})')
} else {
g.writeln('NULL // No attrs')
}
g.indent--
g.writeln(')')
g.indent--
g.write('})')
}
// write_orm_create_table writes C code that calls ORM functions for creating tables. // write_orm_create_table writes C code that calls ORM functions for creating tables.
fn (mut g Gen) write_orm_create_table(node ast.SqlStmtLine, table_name string, connection_var_name string, fn (mut g Gen) write_orm_create_table(node ast.SqlStmtLine, table_name string, connection_var_name string,
result_var_name string) { result_var_name string, table_attrs []ast.Attr) {
g.writeln('// sql { create table `${table_name}` }') g.writeln('// sql { create table `${table_name}` }')
g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_create(') g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_create(')
g.indent++ g.indent++
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('_S("${table_name}"),') g.write_orm_table_struct(node.table_expr.typ)
g.writeln(',')
g.writeln('new_array_from_c_array(${node.fields.len}, ${node.fields.len}, sizeof(orm__TableField),') g.writeln('new_array_from_c_array(${node.fields.len}, ${node.fields.len}, sizeof(orm__TableField),')
g.indent++ g.indent++
@ -213,19 +259,19 @@ fn (mut g Gen) write_orm_create_table(node ast.SqlStmtLine, table_name string, c
} }
// write_orm_drop_table writes C code that calls ORM functions for dropping tables. // write_orm_drop_table writes C code that calls ORM functions for dropping tables.
fn (mut g Gen) write_orm_drop_table(table_name string, connection_var_name string, result_var_name string) { fn (mut g Gen) write_orm_drop_table(node ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, table_attrs []ast.Attr) {
g.writeln('// sql { drop table `${table_name}` }') g.writeln('// sql { drop table `${table_name}` }')
g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_drop(') g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_drop(')
g.indent++ g.indent++
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('_S("${table_name}")') g.write_orm_table_struct(node.table_expr.typ)
g.indent-- g.indent--
g.writeln(');') g.writeln(');')
} }
// write_orm_insert writes C code that calls ORM functions for inserting structs into a table. // write_orm_insert writes C code that calls ORM functions for inserting structs into a table.
fn (mut g Gen) write_orm_insert(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, fn (mut g Gen) write_orm_insert(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string,
or_expr &ast.OrExpr) { or_expr &ast.OrExpr, table_attrs []ast.Attr) {
last_ids_variable_name := g.new_tmp_var() last_ids_variable_name := g.new_tmp_var()
g.writeln('Array_orm__Primitive ${last_ids_variable_name} = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);') g.writeln('Array_orm__Primitive ${last_ids_variable_name} = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);')
@ -234,12 +280,13 @@ fn (mut g Gen) write_orm_insert(node &ast.SqlStmtLine, table_name string, connec
} }
// write_orm_update writes C code that calls ORM functions for updating rows. // write_orm_update writes C code that calls ORM functions for updating rows.
fn (mut g Gen) write_orm_update(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string) { fn (mut g Gen) write_orm_update(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, table_attrs []ast.Attr) {
g.writeln('// sql { update `${table_name}` }') g.writeln('// sql { update `${table_name}` }')
g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_update(') g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method_update(')
g.indent++ g.indent++
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('_S("${table_name}"),') g.write_orm_table_struct(node.table_expr.typ)
g.writeln(',')
g.writeln('(orm__QueryData){') g.writeln('(orm__QueryData){')
g.indent++ g.indent++
g.writeln('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),') g.writeln('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
@ -285,12 +332,13 @@ fn (mut g Gen) write_orm_update(node &ast.SqlStmtLine, table_name string, connec
} }
// write_orm_delete writes C code that calls ORM functions for deleting rows. // write_orm_delete writes C code that calls ORM functions for deleting rows.
fn (mut g Gen) write_orm_delete(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string) { fn (mut g Gen) write_orm_delete(node &ast.SqlStmtLine, table_name string, connection_var_name string, result_var_name string, table_attrs []ast.Attr) {
g.writeln('// sql { delete from `${table_name}` }') g.writeln('// sql { delete from `${table_name}` }')
g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method__v_delete(') g.writeln('${result_name}_void ${result_var_name} = orm__Connection_name_table[${connection_var_name}._typ]._method__v_delete(')
g.indent++ g.indent++
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('_S("${table_name}"),') g.write_orm_table_struct(node.table_expr.typ)
g.writeln(',')
g.write_orm_where(node.where_expr) g.write_orm_where(node.where_expr)
g.indent-- g.indent--
g.writeln(');') g.writeln(');')
@ -381,7 +429,8 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v
g.writeln('${result_name}_void ${res} = orm__Connection_name_table[${connection_var_name}._typ]._method_insert(') g.writeln('${result_name}_void ${res} = orm__Connection_name_table[${connection_var_name}._typ]._method_insert(')
g.indent++ g.indent++
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('_S("${table_name}"),') g.write_orm_table_struct(node.table_expr.typ)
g.writeln(',')
g.writeln('(orm__QueryData){') g.writeln('(orm__QueryData){')
g.indent++ g.indent++
g.writeln('.fields = new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),') g.writeln('.fields = new_array_from_c_array(${fields.len}, ${fields.len}, sizeof(string),')
@ -880,7 +929,6 @@ fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, re
select_result_var_name := g.new_tmp_var() select_result_var_name := g.new_tmp_var()
table_name := g.get_table_name_by_struct_type(node.table_expr.typ) table_name := g.get_table_name_by_struct_type(node.table_expr.typ)
escaped_table_name := cescape_nonascii(util.smart_quote(table_name, false))
g.sql_table_name = g.table.sym(node.table_expr.typ).name g.sql_table_name = g.table.sym(node.table_expr.typ).name
g.writeln('// sql { select from `${table_name}` }') g.writeln('// sql { select from `${table_name}` }')
@ -889,7 +937,9 @@ fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, re
g.writeln('${connection_var_name}._object, // Connection object') g.writeln('${connection_var_name}._object, // Connection object')
g.writeln('(orm__SelectConfig){') g.writeln('(orm__SelectConfig){')
g.indent++ g.indent++
g.writeln('.table = _S("${escaped_table_name}"),') g.writeln('.table = ')
g.write_orm_table_struct(node.table_expr.typ)
g.writeln(',')
g.writeln('.is_count = ${node.is_count},') g.writeln('.is_count = ${node.is_count},')
g.writeln('.has_where = ${node.has_where},') g.writeln('.has_where = ${node.has_where},')
g.writeln('.has_order = ${node.has_order},') g.writeln('.has_order = ${node.has_order},')
@ -1246,6 +1296,13 @@ fn (g &Gen) get_db_expr_type(expr ast.Expr) ?ast.Type {
return none return none
} }
// get_table_attrs_by_struct_type returns the struct attrs.
fn (g &Gen) get_table_attrs_by_struct_type(typ ast.Type) []ast.Attr {
sym := g.table.sym(typ)
info := sym.struct_info()
return info.attrs
}
// get_table_name_by_struct_type converts the struct type to a table name. // get_table_name_by_struct_type converts the struct type to a table name.
fn (g &Gen) get_table_name_by_struct_type(typ ast.Type) string { fn (g &Gen) get_table_name_by_struct_type(typ ast.Type) string {
sym := g.table.sym(typ) sym := g.table.sym(typ)
@ -1255,8 +1312,8 @@ fn (g &Gen) get_table_name_by_struct_type(typ ast.Type) string {
if attr := info.attrs.find_first('table') { if attr := info.attrs.find_first('table') {
table_name = attr.arg table_name = attr.arg
} }
escaped_table_name := cescape_nonascii(util.smart_quote(table_name, false))
return table_name return escaped_table_name
} }
// get_orm_current_table_field returns the current processing table's struct field by name. // get_orm_current_table_field returns the current processing table's struct field by name.