codegen: wrap foreign key checks for nullable FKs in "if a.Val != 0 { ... }"
This commit is contained in:
@@ -161,79 +161,92 @@ func buildFKCheckLambda(tbl schema.Table) (*ast.AssignStmt, bool) {
|
||||
structFieldName := col.GoFieldName()
|
||||
structField := &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(structFieldName)}
|
||||
|
||||
if col.IsNonCodeTableForeignKey() {
|
||||
// Real foreign key; look up referent by ID to see if it exists
|
||||
ret = append(ret, &ast.IfStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent(getByIDFuncName(col.ForeignKeyTargetTable))},
|
||||
Args: []ast.Expr{structField},
|
||||
ret = append(ret, func() ast.Stmt {
|
||||
// Wrap nullable FKs in "if a.val != 0 { ... }"
|
||||
wrap := func(input ast.Stmt) ast.Stmt {
|
||||
if col.IsNullableForeignKey() {
|
||||
return &ast.IfStmt{
|
||||
Cond: &ast.BinaryExpr{X: structField, Op: token.NEQ, Y: &ast.BasicLit{Kind: token.INT, Value: "0"}},
|
||||
Body: &ast.BlockStmt{List: []ast.Stmt{input}},
|
||||
}
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
}
|
||||
if col.IsNonCodeTableForeignKey() {
|
||||
// Real foreign key; look up referent by ID to see if it exists
|
||||
return wrap(&ast.IfStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent(getByIDFuncName(col.ForeignKeyTargetTable))},
|
||||
Args: []ast.Expr{structField},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Cond: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
|
||||
Args: []ast.Expr{ast.NewIdent("err"), ast.NewIdent("ErrNotInDB")},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: ast.NewIdent("NewForeignKeyError"),
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
Cond: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
|
||||
Args: []ast.Expr{ast.NewIdent("err"), ast.NewIdent("ErrNotInDB")},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: ast.NewIdent("NewForeignKeyError"),
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// Code table value. Query the table to see if it exists
|
||||
ret = append(ret, &ast.IfStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("err")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")},
|
||||
Args: []ast.Expr{
|
||||
&ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
})
|
||||
} else {
|
||||
// Code table value. Query the table to see if it exists
|
||||
return wrap(&ast.IfStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("err")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")},
|
||||
Args: []ast.Expr{
|
||||
&ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Cond: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
|
||||
Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: ast.NewIdent("NewForeignKeyError"),
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
Cond: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
|
||||
Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Results: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: ast.NewIdent("NewForeignKeyError"),
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
|
||||
structField,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}())
|
||||
}
|
||||
// final return nil
|
||||
ret = append(ret, &ast.ReturnStmt{Results: []ast.Expr{ast.NewIdent("nil")}})
|
||||
|
||||
Reference in New Issue
Block a user