codegen: add generator for soft-deletion
This commit is contained in:
@@ -296,6 +296,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
||||
updatePairs := make([]string, 0, len(tbl.Columns))
|
||||
|
||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||
hasSoftDelete := tbl.HasSoftDelete()
|
||||
|
||||
// Assemble data for building SQL "insert" and "update" strings
|
||||
for _, col := range tbl.Columns {
|
||||
@@ -312,6 +313,10 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
||||
if col.Name == "created_at" && hasCreatedAt {
|
||||
continue
|
||||
}
|
||||
// Soft-delete state is managed by SoftDeleteXyz(), not by updates
|
||||
if hasSoftDelete && (col.Name == "is_deleted" || col.Name == "deleted_at") {
|
||||
continue
|
||||
}
|
||||
if !col.IsPrimaryKey { // Don't try to update primary key columns (mainly for w/o rowid tables)
|
||||
updatePairs = append(updatePairs, col.Name+"="+val)
|
||||
}
|
||||
@@ -828,6 +833,66 @@ func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
||||
return funcDecl
|
||||
}
|
||||
|
||||
// GenerateSoftDeleteItemFunc produces an AST for the `SoftDeleteXyz()` function, which is only
|
||||
// generated for tables that have both an "is_deleted" and a "deleted_at" column.
|
||||
// E.g., a table with `table.TypeName = "foods"` will produce a "SoftDeleteFood()" function.
|
||||
func GenerateSoftDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
|
||||
whereClauses := []string{}
|
||||
for _, c := range tbl.PrimaryKeyColumns() {
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s = :%s", c.Name, c.Name))
|
||||
}
|
||||
setPairs := []string{"is_deleted = :is_deleted", "deleted_at = :deleted_at"}
|
||||
|
||||
updateStmt := fmt.Sprintf("\n\t update %s\n\t set %s\n\t where %s\n\t",
|
||||
tbl.TableName,
|
||||
strings.Join(setPairs, ",\n\t "),
|
||||
strings.Join(whereClauses, " and "),
|
||||
)
|
||||
|
||||
funcBody := &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
// item.IsDeleted = true
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(tbl.GetColumnByName("is_deleted").GoFieldName())}},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{ast.NewIdent("true")},
|
||||
},
|
||||
// item.DeletedAt = TimestampNow()
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(tbl.GetColumnByName("deleted_at").GoFieldName())}},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}},
|
||||
},
|
||||
// result := must.Get(db.DB.NamedExec(...))
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("result")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{mustCall(&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("NamedExec")},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: "`" + updateStmt + "`"},
|
||||
ast.NewIdent(tbl.VarName),
|
||||
},
|
||||
})},
|
||||
},
|
||||
MustBeRowsAffected(tbl),
|
||||
},
|
||||
}
|
||||
|
||||
return &ast.FuncDecl{
|
||||
Doc: &ast.CommentGroup{List: []*ast.Comment{
|
||||
{Text: fmt.Sprintf("// SoftDelete%s sets deleted-at and marks the item as deleted.", tbl.GoTypeName)},
|
||||
}},
|
||||
Recv: dbRecv,
|
||||
Name: ast.NewIdent("SoftDelete" + tbl.GoTypeName),
|
||||
Type: &ast.FuncType{Params: &ast.FieldList{List: []*ast.Field{{
|
||||
Names: []*ast.Ident{ast.NewIdent(tbl.VarName)},
|
||||
Type: &ast.StarExpr{X: ast.NewIdent(tbl.GoTypeName)},
|
||||
}}}, Results: nil},
|
||||
Body: funcBody,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateSQLFieldsConst produces an AST for the `const xyzSQLFields = ...` string.
|
||||
func GenerateSQLFieldsConst(tbl schema.Table) *ast.GenDecl {
|
||||
columns := make([]string, 0, len(tbl.Columns))
|
||||
|
||||
@@ -69,9 +69,11 @@ func SampleValue(c pkgschema.Column, offset int) ast.Expr {
|
||||
// UpdateTestFields returns the columns for which the test generator should assign its own
|
||||
// sample values, in the MakeXyz() factory and in the create/update test: the same columns
|
||||
// that GenerateSaveItemFunc's "update" branch writes to, minus foreign keys (which can't be
|
||||
// given plausible values here) and the auto-managed "created_at"/"updated_at" columns.
|
||||
// given plausible values here), the auto-managed "created_at"/"updated_at" columns, and the
|
||||
// soft-delete columns (which SoftDeleteXyz() manages).
|
||||
func UpdateTestFields(tbl pkgschema.Table) (ret []pkgschema.Column) {
|
||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||
hasSoftDelete := tbl.HasSoftDelete()
|
||||
for _, c := range tbl.Columns {
|
||||
if c.Name == "rowid" || c.IsPrimaryKey || c.IsForeignKey {
|
||||
continue
|
||||
@@ -82,6 +84,9 @@ func UpdateTestFields(tbl pkgschema.Table) (ret []pkgschema.Column) {
|
||||
if c.Name == "updated_at" && hasUpdatedAt {
|
||||
continue
|
||||
}
|
||||
if hasSoftDelete && (c.Name == "is_deleted" || c.Name == "deleted_at") {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -61,3 +61,18 @@ func (t Table) HasAutoTimestamps() (hasCreatedAt bool, hasUpdatedAt bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasSoftDelete reports whether this table supports soft-deletion, i.e., whether it has both
|
||||
// an "is_deleted" and a "deleted_at" column.
|
||||
func (t Table) HasSoftDelete() bool {
|
||||
var hasIsDeleted, hasDeletedAt bool
|
||||
for _, c := range t.Columns {
|
||||
if c.Name == "is_deleted" && c.Type == "integer" {
|
||||
hasIsDeleted = true
|
||||
}
|
||||
if c.Name == "deleted_at" && c.Type == "integer" {
|
||||
hasDeletedAt = true
|
||||
}
|
||||
}
|
||||
return hasIsDeleted && hasDeletedAt
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user