1
0
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:
Mark aka walkingdevel 2023-05-19 23:24:30 +00:00 committed by GitHub
parent 94217571cd
commit 1e9dcb9b9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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.