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:

committed by
GitHub

parent
94217571cd
commit
1e9dcb9b9e
@ -7,8 +7,8 @@ import v.token
|
|||||||
import v.util
|
import v.util
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fkey_attr_name = 'fkey'
|
|
||||||
v_orm_prefix = 'V ORM'
|
v_orm_prefix = 'V ORM'
|
||||||
|
fkey_attr_name = 'fkey'
|
||||||
connection_interface_name = 'orm.Connection'
|
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) {
|
if !c.check_db_expr(node.db_expr) {
|
||||||
return ast.void_type
|
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 }
|
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
|
old_ts := c.cur_orm_ts
|
||||||
c.cur_orm_ts = *sym
|
c.cur_orm_ts = *table_sym
|
||||||
defer {
|
defer {
|
||||||
c.cur_orm_ts = old_ts
|
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)
|
info := table_sym.info as ast.Struct
|
||||||
return ast.void_type
|
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)
|
||||||
info := sym.info as ast.Struct
|
|
||||||
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name)
|
|
||||||
mut sub_structs := map[int]ast.SqlExpr{}
|
mut sub_structs := map[int]ast.SqlExpr{}
|
||||||
|
|
||||||
for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_
|
for field in non_primitive_fields {
|
||||||
|| (c.table.sym(it.typ).kind == .array
|
typ := c.get_type_of_field_with_related_table(field)
|
||||||
&& 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut n := ast.SqlExpr{
|
mut subquery_expr := ast.SqlExpr{
|
||||||
pos: node.pos
|
pos: node.pos
|
||||||
has_where: true
|
has_where: true
|
||||||
where_expr: ast.None{}
|
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
|
tmp_inside_sql := c.inside_sql
|
||||||
c.sql_expr(mut n)
|
c.sql_expr(mut subquery_expr)
|
||||||
c.inside_sql = tmp_inside_sql
|
c.inside_sql = tmp_inside_sql
|
||||||
|
|
||||||
n.where_expr = ast.InfixExpr{
|
subquery_expr.where_expr = ast.InfixExpr{
|
||||||
op: .eq
|
op: .eq
|
||||||
pos: n.pos
|
pos: subquery_expr.pos
|
||||||
left: ast.Ident{
|
left: ast.Ident{
|
||||||
language: .v
|
language: .v
|
||||||
tok_kind: .eq
|
tok_kind: .eq
|
||||||
@ -99,7 +95,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
|||||||
or_block: ast.OrExpr{}
|
or_block: ast.OrExpr{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub_structs[int(typ)] = n
|
sub_structs[int(typ)] = subquery_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.is_count {
|
if node.is_count {
|
||||||
@ -117,20 +113,20 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
|||||||
if node.has_where {
|
if node.has_where {
|
||||||
c.expr(node.where_expr)
|
c.expr(node.where_expr)
|
||||||
c.check_expr_has_no_fn_calls_with_non_orm_return_type(&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 node.has_order {
|
||||||
if mut node.order_expr is ast.Ident {
|
if mut node.order_expr is ast.Ident {
|
||||||
order_ident_name := node.order_expr.name
|
order_ident_name := node.order_expr.name
|
||||||
|
|
||||||
if !sym.has_field(order_ident_name) {
|
if !table_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}`'),
|
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)
|
node.order_expr.pos)
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
return ast.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,13 +171,22 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
|||||||
defer {
|
defer {
|
||||||
c.inside_sql = false
|
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 }
|
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type }
|
||||||
table_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
|
old_ts := c.cur_orm_ts
|
||||||
c.cur_orm_ts = *table_sym
|
c.cur_orm_ts = *table_sym
|
||||||
defer {
|
defer {
|
||||||
c.cur_orm_ts = old_ts
|
c.cur_orm_ts = old_ts
|
||||||
}
|
}
|
||||||
|
|
||||||
inserting_object_name := node.object_var_name
|
inserting_object_name := node.object_var_name
|
||||||
|
|
||||||
if node.kind == .insert && !node.is_generated {
|
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
|
info := table_sym.info as ast.Struct
|
||||||
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
||||||
mut sub_structs := map[int]ast.SqlStmtLine{}
|
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_
|
for field in non_primitive_fields {
|
||||||
|| (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') {
|
|
||||||
// Delete an uninitialized struct from fields and skip adding the current field
|
// 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.
|
// 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) {
|
if c.check_field_of_inserting_struct_is_uninitialized(node, field.name) {
|
||||||
fields.delete(fields.index(f))
|
fields.delete(fields.index(field))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c.check_orm_struct_field_attributes(f)
|
c.check_orm_struct_field_attributes(field)
|
||||||
|
|
||||||
typ := if c.table.sym(f.typ).kind == .struct_ {
|
typ := c.get_type_of_field_with_related_table(field)
|
||||||
f.typ
|
|
||||||
} else if c.table.sym(f.typ).kind == .array {
|
|
||||||
c.table.sym(f.typ).array_info().elem_type
|
|
||||||
} else {
|
|
||||||
ast.Type(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut object_var_name := '${node.object_var_name}.${f.name}'
|
mut object_var_name := '${node.object_var_name}.${field.name}'
|
||||||
if typ != f.typ {
|
if typ != field.typ {
|
||||||
object_var_name = node.object_var_name
|
object_var_name = node.object_var_name
|
||||||
}
|
}
|
||||||
mut n := ast.SqlStmtLine{
|
|
||||||
|
mut subquery_expr := ast.SqlStmtLine{
|
||||||
pos: node.pos
|
pos: node.pos
|
||||||
kind: node.kind
|
kind: node.kind
|
||||||
table_expr: ast.TypeNode{
|
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
|
object_var_name: object_var_name
|
||||||
is_generated: true
|
is_generated: true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_inside_sql := c.inside_sql
|
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
|
c.inside_sql = tmp_inside_sql
|
||||||
sub_structs[typ] = n
|
sub_structs[typ] = subquery_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
node.fields = fields
|
node.fields = fields
|
||||||
node.sub_structs = sub_structs.move()
|
node.sub_structs = sub_structs.move()
|
||||||
|
|
||||||
for i, column in node.updated_columns {
|
for i, column in node.updated_columns {
|
||||||
x := node.fields.filter(it.name == column)
|
updated_fields := node.fields.filter(it.name == column)
|
||||||
if x.len == 0 {
|
|
||||||
|
if updated_fields.len == 0 {
|
||||||
c.orm_error('type `${table_sym.name}` has no field named `${column}`', node.pos)
|
c.orm_error('type `${table_sym.name}` has no field named `${column}`', node.pos)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
field := x[0]
|
|
||||||
|
field := updated_fields.first()
|
||||||
node.updated_columns[i] = c.fetch_field_name(field)
|
node.updated_columns[i] = c.fetch_field_name(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.kind == .update {
|
if node.kind == .update {
|
||||||
for expr in node.update_exprs {
|
for expr in node.update_exprs {
|
||||||
c.expr(expr)
|
c.expr(expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.where_expr !is ast.EmptyExpr {
|
if node.where_expr !is ast.EmptyExpr {
|
||||||
c.expr(node.where_expr)
|
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 {
|
fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Pos, table_name string) []ast.StructField {
|
||||||
fields := info.fields.filter(
|
fields := info.fields.filter(fn [mut c] (field ast.StructField) bool {
|
||||||
(it.typ in [ast.string_type, ast.bool_type] || int(it.typ) in ast.number_type_idxs
|
is_primitive := field.typ.is_string() || field.typ.is_bool() || field.typ.is_number()
|
||||||
|| c.table.type_symbols[int(it.typ)].kind == .struct_
|
is_struct := c.table.type_symbols[int(field.typ)].kind == .struct_
|
||||||
|| (c.table.sym(it.typ).kind == .array
|
is_array := c.table.sym(field.typ).kind == .array
|
||||||
&& c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_))
|
is_array_with_struct_elements := is_array
|
||||||
&& !it.attrs.contains('skip'))
|
&& 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 {
|
if fields.len == 0 {
|
||||||
c.orm_error('select: empty fields in `${table_name}`', pos)
|
c.orm_error('select: empty fields in `${table_name}`', pos)
|
||||||
return []ast.StructField{}
|
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 {
|
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_type_index := c.table.find_type_idx(checker.connection_interface_name)
|
||||||
connection_typ := ast.Type(connection_type_index)
|
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
|
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
|
// walkingdevel: Now I don't think it's a good solution
|
||||||
// because it only checks structure initialization,
|
// because it only checks structure initialization,
|
||||||
// but structure fields may be updated later before inserting.
|
// but structure fields may be updated later before inserting.
|
||||||
|
Reference in New Issue
Block a user