diff --git a/ops/gas_init_test.sh b/ops/gas_init_test.sh index 65ddf96..483a1c1 100755 --- a/ops/gas_init_test.sh +++ b/ops/gas_init_test.sh @@ -40,7 +40,9 @@ create table item_flavor (rowid integer primary key, name text not null) strict; create table items ( rowid integer primary key, description text not null default '', - flavor integer references item_flavor(rowid) + flavor integer references item_flavor(rowid), + created_at integer not null, + updated_at integer not null ) strict; EOF diff --git a/pkg/codegen/modelgenerate/generate_model.go b/pkg/codegen/modelgenerate/generate_model.go index 2ee7d86..72b12a6 100644 --- a/pkg/codegen/modelgenerate/generate_model.go +++ b/pkg/codegen/modelgenerate/generate_model.go @@ -97,6 +97,9 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { insertVals := make([]string, 0, len(tbl.Columns)) updatePairs := make([]string, 0, len(tbl.Columns)) + hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps() + + // Assemble data for building SQL "insert" and "update" strings for _, col := range tbl.Columns { if col.Name == "rowid" { continue @@ -107,8 +110,14 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { val = fmt.Sprintf("nullif(%s, 0)", val) } insertVals = append(insertVals, val) + // created_at should not be updated after creation + if col.Name == "created_at" && hasCreatedAt { + continue + } updatePairs = append(updatePairs, col.Name+"="+val) } + insertStmt := fmt.Sprintf("\n\t\t insert into %s (%s)\n\t\t values (%s)\n\t\t", tbl.TableName, strings.Join(insertCols, ", "), strings.Join(insertVals, ", ")) + updateStmt := fmt.Sprintf("\n\t\t update %s\n\t\t set %s\n\t\t where rowid = :rowid\n\t\t", tbl.TableName, strings.Join(updatePairs, ",\n\t\t ")) hasFks := false checkForeignKeyFailuresAssignment := &ast.AssignStmt{ @@ -235,15 +244,20 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { }, } - insertStmt := fmt.Sprintf("\n\t\t insert into %s (%s)\n\t\t values (%s)\n\t\t", tbl.TableName, strings.Join(insertCols, ", "), strings.Join(insertVals, ", ")) - updateStmt := fmt.Sprintf("\n\t\t update %s\n\t\t set %s\n\t\t where rowid = :rowid\n\t\t", tbl.TableName, strings.Join(updatePairs, ",\n\t\t ")) - funcBody := &ast.BlockStmt{ List: func() []ast.Stmt { ret := []ast.Stmt{} if hasFks { ret = append(ret, checkForeignKeyFailuresAssignment) } + if hasUpdatedAt { + // Auto-timestamps: updated_at + ret = append(ret, &ast.AssignStmt{ + Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("UpdatedAt")}}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}}, + }) + } // if item.ID == 0 {...} else {...} ret = append(ret, &ast.IfStmt{ Cond: &ast.BinaryExpr{ @@ -255,6 +269,15 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { // Do create List: append( func() []ast.Stmt { + ret := []ast.Stmt{} + if hasCreatedAt { + // Auto-timestamps: created_at + ret = append(ret, &ast.AssignStmt{ + Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}}, + }) + } namedExecStmt := &ast.CallExpr{ Fun: &ast.SelectorExpr{X: ast.NewIdent("db.DB"), Sel: ast.NewIdent("NamedExec")}, Args: []ast.Expr{ @@ -264,19 +287,17 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { } if !hasFks { // No foreign key checking needed; just use `Must` for brevity - return []ast.Stmt{ - &ast.AssignStmt{ - Lhs: []ast.Expr{ast.NewIdent("result")}, - Tok: token.DEFINE, - Rhs: []ast.Expr{&ast.CallExpr{ - Fun: ast.NewIdent("Must"), - Args: []ast.Expr{namedExecStmt}, - }}, - }, - } + return append(ret, &ast.AssignStmt{ + Lhs: []ast.Expr{ast.NewIdent("result")}, + Tok: token.DEFINE, + Rhs: []ast.Expr{&ast.CallExpr{ + Fun: ast.NewIdent("Must"), + Args: []ast.Expr{namedExecStmt}, + }}, + }) } - return []ast.Stmt{ + return append(ret, // result, err := db.DB.NamedExec(`...`, u) &ast.AssignStmt{ Lhs: []ast.Expr{ @@ -329,7 +350,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl { }, }, }, - } + ) }(), &ast.AssignStmt{ Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}}, diff --git a/pkg/schema/table.go b/pkg/schema/table.go index 2d9e741..6c95766 100644 --- a/pkg/schema/table.go +++ b/pkg/schema/table.go @@ -46,6 +46,18 @@ type Table struct { GoTypeName string } +func (t Table) HasAutoTimestamps() (hasCreatedAt bool, hasUpdatedAt bool) { + for _, c := range t.Columns { + if c.Name == "created_at" && c.Type == "integer" { + hasCreatedAt = true + } + if c.Name == "updated_at" && c.Type == "integer" { + hasUpdatedAt = true + } + } + return +} + type Index struct { Name string `db:"index_name"` TableName string `db:"table_name"`