mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: refactor, comments, simplify. (#18203)
This commit is contained in:
parent
94217571cd
commit
1e9dcb9b9e
@ -7,8 +7,8 @@ import v.token
|
||||
import v.util
|
||||
|
||||
const (
|
||||
fkey_attr_name = 'fkey'
|
||||
v_orm_prefix = 'V ORM'
|
||||
fkey_attr_name = 'fkey'
|
||||
connection_interface_name = 'orm.Connection'
|
||||
)
|
||||
|
||||
@ -23,35 +23,31 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
||||
if !c.check_db_expr(node.db_expr) {
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
// To avoid panics while working with `table_expr`,
|
||||
// it is necessary to check if its type exists.
|
||||
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type }
|
||||
sym := c.table.sym(node.table_expr.typ)
|
||||
table_sym := c.table.sym(node.table_expr.typ)
|
||||
|
||||
if !c.check_orm_table_expr_type(node.table_expr) {
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
old_ts := c.cur_orm_ts
|
||||
c.cur_orm_ts = *sym
|
||||
c.cur_orm_ts = *table_sym
|
||||
defer {
|
||||
c.cur_orm_ts = old_ts
|
||||
}
|
||||
if sym.info !is ast.Struct {
|
||||
c.orm_error('the table symbol `${sym.name}` has to be a struct', node.table_expr.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
info := sym.info as ast.Struct
|
||||
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name)
|
||||
|
||||
info := table_sym.info as ast.Struct
|
||||
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
||||
non_primitive_fields := c.get_orm_non_primitive_fields(fields)
|
||||
mut sub_structs := map[int]ast.SqlExpr{}
|
||||
|
||||
for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_
|
||||
|| (c.table.sym(it.typ).kind == .array
|
||||
&& c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_))
|
||||
&& c.table.get_type_name(it.typ) != 'time.Time') {
|
||||
typ := if c.table.sym(f.typ).kind == .struct_ {
|
||||
f.typ
|
||||
} else if c.table.sym(f.typ).kind == .array {
|
||||
c.table.sym(f.typ).array_info().elem_type
|
||||
} else {
|
||||
ast.Type(0)
|
||||
}
|
||||
for field in non_primitive_fields {
|
||||
typ := c.get_type_of_field_with_related_table(field)
|
||||
|
||||
mut n := ast.SqlExpr{
|
||||
mut subquery_expr := ast.SqlExpr{
|
||||
pos: node.pos
|
||||
has_where: true
|
||||
where_expr: ast.None{}
|
||||
@ -65,12 +61,12 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
||||
}
|
||||
|
||||
tmp_inside_sql := c.inside_sql
|
||||
c.sql_expr(mut n)
|
||||
c.sql_expr(mut subquery_expr)
|
||||
c.inside_sql = tmp_inside_sql
|
||||
|
||||
n.where_expr = ast.InfixExpr{
|
||||
subquery_expr.where_expr = ast.InfixExpr{
|
||||
op: .eq
|
||||
pos: n.pos
|
||||
pos: subquery_expr.pos
|
||||
left: ast.Ident{
|
||||
language: .v
|
||||
tok_kind: .eq
|
||||
@ -99,7 +95,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
||||
or_block: ast.OrExpr{}
|
||||
}
|
||||
|
||||
sub_structs[int(typ)] = n
|
||||
sub_structs[int(typ)] = subquery_expr
|
||||
}
|
||||
|
||||
if node.is_count {
|
||||
@ -117,20 +113,20 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
||||
if node.has_where {
|
||||
c.expr(node.where_expr)
|
||||
c.check_expr_has_no_fn_calls_with_non_orm_return_type(&node.where_expr)
|
||||
c.check_where_expr_has_no_pointless_exprs(sym, field_names, &node.where_expr)
|
||||
c.check_where_expr_has_no_pointless_exprs(table_sym, field_names, &node.where_expr)
|
||||
}
|
||||
|
||||
if node.has_order {
|
||||
if mut node.order_expr is ast.Ident {
|
||||
order_ident_name := node.order_expr.name
|
||||
|
||||
if !sym.has_field(order_ident_name) {
|
||||
c.orm_error(util.new_suggestion(order_ident_name, field_names).say('`${sym.name}` structure has no field with name `${order_ident_name}`'),
|
||||
if !table_sym.has_field(order_ident_name) {
|
||||
c.orm_error(util.new_suggestion(order_ident_name, field_names).say('`${table_sym.name}` structure has no field with name `${order_ident_name}`'),
|
||||
node.order_expr.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
} else {
|
||||
c.orm_error("expected `${sym.name}` structure's field", node.order_expr.pos())
|
||||
c.orm_error("expected `${table_sym.name}` structure's field", node.order_expr.pos())
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
@ -175,13 +171,22 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
||||
defer {
|
||||
c.inside_sql = false
|
||||
}
|
||||
|
||||
// To avoid panics while working with `table_expr`,
|
||||
// it is necessary to check if its type exists.
|
||||
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type }
|
||||
table_sym := c.table.sym(node.table_expr.typ)
|
||||
|
||||
if !c.check_orm_table_expr_type(node.table_expr) {
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
old_ts := c.cur_orm_ts
|
||||
c.cur_orm_ts = *table_sym
|
||||
defer {
|
||||
c.cur_orm_ts = old_ts
|
||||
}
|
||||
|
||||
inserting_object_name := node.object_var_name
|
||||
|
||||
if node.kind == .insert && !node.is_generated {
|
||||
@ -212,33 +217,26 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
||||
info := table_sym.info as ast.Struct
|
||||
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
||||
mut sub_structs := map[int]ast.SqlStmtLine{}
|
||||
non_primitive_fields := c.get_orm_non_primitive_fields(fields)
|
||||
|
||||
for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_
|
||||
|| (c.table.sym(it.typ).kind == .array
|
||||
&& c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_))
|
||||
&& c.table.get_type_name(it.typ) != 'time.Time') {
|
||||
for field in non_primitive_fields {
|
||||
// Delete an uninitialized struct from fields and skip adding the current field
|
||||
// to sub structs to skip inserting an empty struct in the related table.
|
||||
if c.check_field_of_inserting_struct_is_uninitialized(node, f.name) {
|
||||
fields.delete(fields.index(f))
|
||||
if c.check_field_of_inserting_struct_is_uninitialized(node, field.name) {
|
||||
fields.delete(fields.index(field))
|
||||
continue
|
||||
}
|
||||
|
||||
c.check_orm_struct_field_attributes(f)
|
||||
c.check_orm_struct_field_attributes(field)
|
||||
|
||||
typ := if c.table.sym(f.typ).kind == .struct_ {
|
||||
f.typ
|
||||
} else if c.table.sym(f.typ).kind == .array {
|
||||
c.table.sym(f.typ).array_info().elem_type
|
||||
} else {
|
||||
ast.Type(0)
|
||||
}
|
||||
typ := c.get_type_of_field_with_related_table(field)
|
||||
|
||||
mut object_var_name := '${node.object_var_name}.${f.name}'
|
||||
if typ != f.typ {
|
||||
mut object_var_name := '${node.object_var_name}.${field.name}'
|
||||
if typ != field.typ {
|
||||
object_var_name = node.object_var_name
|
||||
}
|
||||
mut n := ast.SqlStmtLine{
|
||||
|
||||
mut subquery_expr := ast.SqlStmtLine{
|
||||
pos: node.pos
|
||||
kind: node.kind
|
||||
table_expr: ast.TypeNode{
|
||||
@ -248,27 +246,34 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
||||
object_var_name: object_var_name
|
||||
is_generated: true
|
||||
}
|
||||
|
||||
tmp_inside_sql := c.inside_sql
|
||||
c.sql_stmt_line(mut n)
|
||||
c.sql_stmt_line(mut subquery_expr)
|
||||
c.inside_sql = tmp_inside_sql
|
||||
sub_structs[typ] = n
|
||||
sub_structs[typ] = subquery_expr
|
||||
}
|
||||
|
||||
node.fields = fields
|
||||
node.sub_structs = sub_structs.move()
|
||||
|
||||
for i, column in node.updated_columns {
|
||||
x := node.fields.filter(it.name == column)
|
||||
if x.len == 0 {
|
||||
updated_fields := node.fields.filter(it.name == column)
|
||||
|
||||
if updated_fields.len == 0 {
|
||||
c.orm_error('type `${table_sym.name}` has no field named `${column}`', node.pos)
|
||||
continue
|
||||
}
|
||||
field := x[0]
|
||||
|
||||
field := updated_fields.first()
|
||||
node.updated_columns[i] = c.fetch_field_name(field)
|
||||
}
|
||||
|
||||
if node.kind == .update {
|
||||
for expr in node.update_exprs {
|
||||
c.expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
if node.where_expr !is ast.EmptyExpr {
|
||||
c.expr(node.where_expr)
|
||||
}
|
||||
@ -323,12 +328,17 @@ fn (mut c Checker) check_orm_struct_field_attributes(field ast.StructField) {
|
||||
}
|
||||
|
||||
fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Pos, table_name string) []ast.StructField {
|
||||
fields := info.fields.filter(
|
||||
(it.typ in [ast.string_type, ast.bool_type] || int(it.typ) in ast.number_type_idxs
|
||||
|| c.table.type_symbols[int(it.typ)].kind == .struct_
|
||||
|| (c.table.sym(it.typ).kind == .array
|
||||
&& c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_))
|
||||
&& !it.attrs.contains('skip'))
|
||||
fields := info.fields.filter(fn [mut c] (field ast.StructField) bool {
|
||||
is_primitive := field.typ.is_string() || field.typ.is_bool() || field.typ.is_number()
|
||||
is_struct := c.table.type_symbols[int(field.typ)].kind == .struct_
|
||||
is_array := c.table.sym(field.typ).kind == .array
|
||||
is_array_with_struct_elements := is_array
|
||||
&& c.table.sym(c.table.sym(field.typ).array_info().elem_type).kind == .struct_
|
||||
has_no_skip_attr := !field.attrs.contains('skip')
|
||||
|
||||
return (is_primitive || is_struct || is_array_with_struct_elements) && has_no_skip_attr
|
||||
})
|
||||
|
||||
if fields.len == 0 {
|
||||
c.orm_error('select: empty fields in `${table_name}`', pos)
|
||||
return []ast.StructField{}
|
||||
@ -512,6 +522,7 @@ fn (mut c Checker) check_orm_or_expr(expr ORMExpr) {
|
||||
}
|
||||
}
|
||||
|
||||
// check_db_expr checks the `db_expr` implements `orm.Connection` and has no `option` flag.
|
||||
fn (mut c Checker) check_db_expr(db_expr &ast.Expr) bool {
|
||||
connection_type_index := c.table.find_type_idx(checker.connection_interface_name)
|
||||
connection_typ := ast.Type(connection_type_index)
|
||||
@ -536,6 +547,46 @@ fn (mut c Checker) check_db_expr(db_expr &ast.Expr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_orm_table_expr_type(type_node &ast.TypeNode) bool {
|
||||
table_sym := c.table.sym(type_node.typ)
|
||||
|
||||
if table_sym.info !is ast.Struct {
|
||||
c.orm_error('the table symbol `${table_sym.name}` has to be a struct', type_node.pos)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// get_type_of_field_with_related_table gets the type of table in which
|
||||
// the primary key is used as the foreign key in the current table.
|
||||
// For example, if you are using `[]Child`, the related table type would be `Child`.
|
||||
fn (c &Checker) get_type_of_field_with_related_table(table_field &ast.StructField) ast.Type {
|
||||
if c.table.sym(table_field.typ).kind == .struct_ {
|
||||
return table_field.typ
|
||||
} else if c.table.sym(table_field.typ).kind == .array {
|
||||
return c.table.sym(table_field.typ).array_info().elem_type
|
||||
} else {
|
||||
return ast.Type(0)
|
||||
}
|
||||
}
|
||||
|
||||
// get_orm_non_primitive_fields filters the table fields by selecting only
|
||||
// non-primitive fields such as arrays and structs.
|
||||
fn (c &Checker) get_orm_non_primitive_fields(fields []ast.StructField) []ast.StructField {
|
||||
return fields.filter(fn [c] (field ast.StructField) bool {
|
||||
type_with_no_option_flag := field.typ.clear_flag(.option)
|
||||
is_struct := c.table.type_symbols[int(type_with_no_option_flag)].kind == .struct_
|
||||
is_array := c.table.sym(type_with_no_option_flag).kind == .array
|
||||
is_array_with_struct_elements := is_array
|
||||
&& c.table.sym(c.table.sym(type_with_no_option_flag).array_info().elem_type).kind == .struct_
|
||||
is_time := c.table.get_type_name(type_with_no_option_flag) == 'time.Time'
|
||||
|
||||
return (is_struct || is_array_with_struct_elements) && !is_time
|
||||
})
|
||||
}
|
||||
|
||||
// walkingdevel: Now I don't think it's a good solution
|
||||
// because it only checks structure initialization,
|
||||
// but structure fields may be updated later before inserting.
|
||||
|
Loading…
Reference in New Issue
Block a user