Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ba20e0581 |
+17
-5
@@ -16,6 +16,7 @@ import (
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/drawdb"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/json"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/pgsql"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/sqldir"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/sqlite"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers/yaml"
|
||||
)
|
||||
@@ -87,11 +88,11 @@ Examples:
|
||||
}
|
||||
|
||||
func init() {
|
||||
diffCmd.Flags().StringVar(&sourceType, "from", "", "Source database format (dbml, dctx, drawdb, json, yaml, pgsql)")
|
||||
diffCmd.Flags().StringVar(&sourceType, "from", "", "Source database format (dbml, dctx, drawdb, json, yaml, pgsql, sqldir)")
|
||||
diffCmd.Flags().StringVar(&sourcePath, "from-path", "", "Source file path (for file-based formats)")
|
||||
diffCmd.Flags().StringVar(&sourceConn, "from-conn", "", "Source connection string (for database formats)")
|
||||
|
||||
diffCmd.Flags().StringVar(&targetType, "to", "", "Target database format (dbml, dctx, drawdb, json, yaml, pgsql)")
|
||||
diffCmd.Flags().StringVar(&targetType, "to", "", "Target database format (dbml, dctx, drawdb, json, yaml, pgsql, sqldir)")
|
||||
diffCmd.Flags().StringVar(&targetPath, "to-path", "", "Target file path (for file-based formats)")
|
||||
diffCmd.Flags().StringVar(&targetConn, "to-conn", "", "Target connection string (for database formats)")
|
||||
|
||||
@@ -129,10 +130,12 @@ func runDiff(cmd *cobra.Command, args []string) error {
|
||||
|
||||
fmt.Fprintf(os.Stderr, " ✓ Successfully read database '%s'\n", sourceDB.Name)
|
||||
sourceTables := 0
|
||||
sourceScripts := 0
|
||||
for _, schema := range sourceDB.Schemas {
|
||||
sourceTables += len(schema.Tables)
|
||||
sourceScripts += len(schema.Scripts)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, " Found: %d schema(s), %d table(s)\n\n", len(sourceDB.Schemas), sourceTables)
|
||||
fmt.Fprintf(os.Stderr, " Found: %d schema(s), %d table(s), %d script(s)\n\n", len(sourceDB.Schemas), sourceTables, sourceScripts)
|
||||
|
||||
// Read target database
|
||||
fmt.Fprintf(os.Stderr, "[2/3] Reading target schema...\n")
|
||||
@@ -151,10 +154,12 @@ func runDiff(cmd *cobra.Command, args []string) error {
|
||||
|
||||
fmt.Fprintf(os.Stderr, " ✓ Successfully read database '%s'\n", targetDB.Name)
|
||||
targetTables := 0
|
||||
targetScripts := 0
|
||||
for _, schema := range targetDB.Schemas {
|
||||
targetTables += len(schema.Tables)
|
||||
targetScripts += len(schema.Scripts)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, " Found: %d schema(s), %d table(s)\n\n", len(targetDB.Schemas), targetTables)
|
||||
fmt.Fprintf(os.Stderr, " Found: %d schema(s), %d table(s), %d script(s)\n\n", len(targetDB.Schemas), targetTables, targetScripts)
|
||||
|
||||
// Compare databases
|
||||
fmt.Fprintf(os.Stderr, "[3/3] Comparing schemas...\n")
|
||||
@@ -165,7 +170,8 @@ func runDiff(cmd *cobra.Command, args []string) error {
|
||||
summary.Tables.Missing + summary.Tables.Extra + summary.Tables.Modified +
|
||||
summary.Columns.Missing + summary.Columns.Extra + summary.Columns.Modified +
|
||||
summary.Indexes.Missing + summary.Indexes.Extra + summary.Indexes.Modified +
|
||||
summary.Constraints.Missing + summary.Constraints.Extra + summary.Constraints.Modified
|
||||
summary.Constraints.Missing + summary.Constraints.Extra + summary.Constraints.Modified +
|
||||
summary.Scripts.Missing + summary.Scripts.Extra + summary.Scripts.Modified
|
||||
|
||||
fmt.Fprintf(os.Stderr, " ✓ Comparison complete\n")
|
||||
fmt.Fprintf(os.Stderr, " Found: %d difference(s)\n\n", totalDiffs)
|
||||
@@ -249,6 +255,12 @@ func readDatabase(dbType, filePath, connString, label string) (*models.Database,
|
||||
}
|
||||
reader = yaml.NewReader(&readers.ReaderOptions{FilePath: filePath})
|
||||
|
||||
case "sqldir", "scripts", "scriptdir":
|
||||
if filePath == "" {
|
||||
return nil, fmt.Errorf("%s: file path is required for SQL directory format", label)
|
||||
}
|
||||
reader = sqldir.NewReader(&readers.ReaderOptions{FilePath: filePath})
|
||||
|
||||
case "pgsql", "postgres", "postgresql":
|
||||
if connString == "" {
|
||||
return nil, fmt.Errorf("%s: connection string is required for PostgreSQL format", label)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadDatabaseSupportsSQLDir(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(tempDir, "1_001_create_users.sql"), []byte("CREATE TABLE users (id int);"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(tempDir, "1_002_seed_users.pgsql"), []byte("INSERT INTO users (id) VALUES (1);"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db, err := readDatabase("sqldir", tempDir, "", "source")
|
||||
if err != nil {
|
||||
t.Fatalf("readDatabase failed: %v", err)
|
||||
}
|
||||
if len(db.Schemas) != 1 {
|
||||
t.Fatalf("expected 1 schema, got %d", len(db.Schemas))
|
||||
}
|
||||
if got := len(db.Schemas[0].Scripts); got != 2 {
|
||||
t.Fatalf("expected 2 scripts, got %d", got)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package diff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
@@ -96,6 +97,13 @@ func compareSchemaDetails(source, target *models.Schema) *SchemaChange {
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
// Compare scripts
|
||||
scriptDiff := compareScripts(source.Scripts, target.Scripts)
|
||||
if !isEmpty(scriptDiff) {
|
||||
change.Scripts = scriptDiff
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if !hasChanges {
|
||||
return nil
|
||||
}
|
||||
@@ -543,6 +551,79 @@ func compareSequenceDetails(source, target *models.Sequence) map[string]any {
|
||||
return changes
|
||||
}
|
||||
|
||||
func compareScripts(source, target []*models.Script) *ScriptDiff {
|
||||
diff := &ScriptDiff{
|
||||
Missing: make([]*models.Script, 0),
|
||||
Extra: make([]*models.Script, 0),
|
||||
Modified: make([]*ScriptChange, 0),
|
||||
}
|
||||
|
||||
sourceMap := make(map[string]*models.Script)
|
||||
targetMap := make(map[string]*models.Script)
|
||||
|
||||
for _, s := range source {
|
||||
sourceMap[scriptCompareKey(s)] = s
|
||||
}
|
||||
for _, s := range target {
|
||||
targetMap[scriptCompareKey(s)] = s
|
||||
}
|
||||
|
||||
for _, name := range sortedKeys(sourceMap) {
|
||||
srcScript := sourceMap[name]
|
||||
if tgtScript, exists := targetMap[name]; !exists {
|
||||
diff.Missing = append(diff.Missing, srcScript)
|
||||
} else if changes := compareScriptDetails(srcScript, tgtScript); len(changes) > 0 {
|
||||
diff.Modified = append(diff.Modified, &ScriptChange{
|
||||
Name: srcScript.Name,
|
||||
Source: srcScript,
|
||||
Target: tgtScript,
|
||||
Changes: changes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range sortedKeys(targetMap) {
|
||||
tgtScript := targetMap[name]
|
||||
if _, exists := sourceMap[name]; !exists {
|
||||
diff.Extra = append(diff.Extra, tgtScript)
|
||||
}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func scriptCompareKey(script *models.Script) string {
|
||||
return fmt.Sprintf("%d:%d:%s", script.Priority, script.Sequence, script.SQLName())
|
||||
}
|
||||
|
||||
func compareScriptDetails(source, target *models.Script) map[string]any {
|
||||
changes := make(map[string]any)
|
||||
|
||||
if source.SQL != target.SQL {
|
||||
changes["sql"] = map[string]string{"source": source.SQL, "target": target.SQL}
|
||||
}
|
||||
if source.Rollback != target.Rollback {
|
||||
changes["rollback"] = map[string]string{"source": source.Rollback, "target": target.Rollback}
|
||||
}
|
||||
if !reflect.DeepEqual(source.RunAfter, target.RunAfter) {
|
||||
changes["run_after"] = map[string][]string{"source": source.RunAfter, "target": target.RunAfter}
|
||||
}
|
||||
if source.Schema != target.Schema {
|
||||
changes["schema"] = map[string]string{"source": source.Schema, "target": target.Schema}
|
||||
}
|
||||
if source.Version != target.Version {
|
||||
changes["version"] = map[string]string{"source": source.Version, "target": target.Version}
|
||||
}
|
||||
if source.Priority != target.Priority {
|
||||
changes["priority"] = map[string]int{"source": source.Priority, "target": target.Priority}
|
||||
}
|
||||
if source.Sequence != target.Sequence {
|
||||
changes["sequence"] = map[string]uint{"source": source.Sequence, "target": target.Sequence}
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
// Helper function to check if a diff is empty
|
||||
func isEmpty(v any) bool {
|
||||
switch d := v.(type) {
|
||||
@@ -560,6 +641,8 @@ func isEmpty(v any) bool {
|
||||
return len(d.Missing) == 0 && len(d.Extra) == 0 && len(d.Modified) == 0
|
||||
case *SequenceDiff:
|
||||
return len(d.Missing) == 0 && len(d.Extra) == 0 && len(d.Modified) == 0
|
||||
case *ScriptDiff:
|
||||
return len(d.Missing) == 0 && len(d.Extra) == 0 && len(d.Modified) == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -616,6 +699,11 @@ func ComputeSummary(result *DiffResult) *Summary {
|
||||
summary.Sequences.Extra += len(schemaChange.Sequences.Extra)
|
||||
summary.Sequences.Modified += len(schemaChange.Sequences.Modified)
|
||||
}
|
||||
if schemaChange.Scripts != nil {
|
||||
summary.Scripts.Missing += len(schemaChange.Scripts.Missing)
|
||||
summary.Scripts.Extra += len(schemaChange.Scripts.Extra)
|
||||
summary.Scripts.Modified += len(schemaChange.Scripts.Modified)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -525,6 +525,78 @@ func TestCompareSchemas(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareScripts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
source []*models.Script
|
||||
target []*models.Script
|
||||
want func(*ScriptDiff) bool
|
||||
}{
|
||||
{
|
||||
name: "identical scripts",
|
||||
source: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id int);", Priority: 1, Sequence: 1}},
|
||||
target: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id int);", Priority: 1, Sequence: 1}},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Missing) == 0 && len(d.Extra) == 0 && len(d.Modified) == 0
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing script",
|
||||
source: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id int);"}},
|
||||
target: []*models.Script{},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Missing) == 1 && d.Missing[0].Name == "create_users"
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "extra script",
|
||||
source: []*models.Script{},
|
||||
target: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id int);"}},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Extra) == 1 && d.Extra[0].Name == "create_users"
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "modified script sql",
|
||||
source: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id int);"}},
|
||||
target: []*models.Script{{Name: "create_users", SQL: "CREATE TABLE users (id bigint);"}},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Modified) == 1 && d.Modified[0].Name == "create_users" && d.Modified[0].Changes["sql"] != nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different script order is different identity",
|
||||
source: []*models.Script{{Name: "create_users", SQL: "SELECT 1;", Priority: 1, Sequence: 1}},
|
||||
target: []*models.Script{{Name: "create_users", SQL: "SELECT 1;", Priority: 2, Sequence: 3}},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Missing) == 1 && len(d.Extra) == 1 && len(d.Modified) == 0
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same descriptive names remain distinct",
|
||||
source: []*models.Script{
|
||||
{Name: "alter_users", SQL: "SELECT 1;", Priority: 1, Sequence: 1},
|
||||
{Name: "alter_users", SQL: "SELECT 2;", Priority: 1, Sequence: 2},
|
||||
},
|
||||
target: []*models.Script{
|
||||
{Name: "alter_users", SQL: "SELECT 1;", Priority: 1, Sequence: 1},
|
||||
},
|
||||
want: func(d *ScriptDiff) bool {
|
||||
return len(d.Missing) == 1 && d.Missing[0].Sequence == 2
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := compareScripts(tt.source, tt.target)
|
||||
if !tt.want(got) {
|
||||
t.Errorf("compareScripts() result doesn't match expectations")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -540,6 +612,8 @@ func TestIsEmpty(t *testing.T) {
|
||||
{"TableDiff with extra", &TableDiff{Missing: []*models.Table{}, Extra: []*models.Table{{Name: "users"}}, Modified: []*TableChange{}}, false},
|
||||
{"empty ConstraintDiff", &ConstraintDiff{Missing: []*models.Constraint{}, Extra: []*models.Constraint{}, Modified: []*ConstraintChange{}}, true},
|
||||
{"empty RelationshipDiff", &RelationshipDiff{Missing: []*models.Relationship{}, Extra: []*models.Relationship{}, Modified: []*RelationshipChange{}}, true},
|
||||
{"empty ScriptDiff", &ScriptDiff{Missing: []*models.Script{}, Extra: []*models.Script{}, Modified: []*ScriptChange{}}, true},
|
||||
{"ScriptDiff with modified", &ScriptDiff{Missing: []*models.Script{}, Extra: []*models.Script{}, Modified: []*ScriptChange{{Name: "create_users"}}}, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -586,6 +660,26 @@ func TestComputeSummary(t *testing.T) {
|
||||
return s.Schemas.Missing == 1 && s.Schemas.Extra == 2 && s.Schemas.Modified == 1
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scripts with differences",
|
||||
result: &DiffResult{
|
||||
Schemas: &SchemaDiff{
|
||||
Modified: []*SchemaChange{
|
||||
{
|
||||
Name: "public",
|
||||
Scripts: &ScriptDiff{
|
||||
Missing: []*models.Script{{Name: "missing_script"}},
|
||||
Extra: []*models.Script{{Name: "extra_script"}, {Name: "seed_data"}},
|
||||
Modified: []*ScriptChange{{Name: "changed_script"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: func(s *Summary) bool {
|
||||
return s.Scripts.Missing == 1 && s.Scripts.Extra == 2 && s.Scripts.Modified == 1
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
+66
-1
@@ -158,6 +158,21 @@ func formatSummary(result *DiffResult, w io.Writer) error {
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
// Scripts
|
||||
if summary.Scripts.Missing > 0 || summary.Scripts.Extra > 0 || summary.Scripts.Modified > 0 {
|
||||
fmt.Fprintf(w, "Scripts:\n")
|
||||
if summary.Scripts.Missing > 0 {
|
||||
fmt.Fprintf(w, " Missing: %d\n", summary.Scripts.Missing)
|
||||
}
|
||||
if summary.Scripts.Extra > 0 {
|
||||
fmt.Fprintf(w, " Extra: %d\n", summary.Scripts.Extra)
|
||||
}
|
||||
if summary.Scripts.Modified > 0 {
|
||||
fmt.Fprintf(w, " Modified: %d\n", summary.Scripts.Modified)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
// Check if there are no differences
|
||||
if summary.Schemas.Missing == 0 && summary.Schemas.Extra == 0 && summary.Schemas.Modified == 0 &&
|
||||
summary.Tables.Missing == 0 && summary.Tables.Extra == 0 && summary.Tables.Modified == 0 &&
|
||||
@@ -166,7 +181,8 @@ func formatSummary(result *DiffResult, w io.Writer) error {
|
||||
summary.Constraints.Missing == 0 && summary.Constraints.Extra == 0 && summary.Constraints.Modified == 0 &&
|
||||
summary.Relationships.Missing == 0 && summary.Relationships.Extra == 0 && summary.Relationships.Modified == 0 &&
|
||||
summary.Views.Missing == 0 && summary.Views.Extra == 0 && summary.Views.Modified == 0 &&
|
||||
summary.Sequences.Missing == 0 && summary.Sequences.Extra == 0 && summary.Sequences.Modified == 0 {
|
||||
summary.Sequences.Missing == 0 && summary.Sequences.Extra == 0 && summary.Sequences.Modified == 0 &&
|
||||
summary.Scripts.Missing == 0 && summary.Scripts.Extra == 0 && summary.Scripts.Modified == 0 {
|
||||
fmt.Fprintf(w, "No differences found.\n")
|
||||
}
|
||||
|
||||
@@ -448,6 +464,26 @@ const htmlTemplate = `<!DOCTYPE html>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if or .Summary.Scripts.Missing .Summary.Scripts.Extra .Summary.Scripts.Modified}}
|
||||
<div class="summary-item">
|
||||
<h3>Scripts</h3>
|
||||
<div class="count-group">
|
||||
<div class="count">
|
||||
<span class="count-label">Missing</span>
|
||||
<span class="count-value missing">{{.Summary.Scripts.Missing}}</span>
|
||||
</div>
|
||||
<div class="count">
|
||||
<span class="count-label">Extra</span>
|
||||
<span class="count-value extra">{{.Summary.Scripts.Extra}}</span>
|
||||
</div>
|
||||
<div class="count">
|
||||
<span class="count-label">Modified</span>
|
||||
<span class="count-value modified">{{.Summary.Scripts.Modified}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -588,6 +624,35 @@ const htmlTemplate = `<!DOCTYPE html>
|
||||
</ul>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if .Scripts}}
|
||||
{{if .Scripts.Missing}}
|
||||
<h4>Missing Scripts</h4>
|
||||
<ul class="item-list">
|
||||
{{range .Scripts.Missing}}
|
||||
<li class="missing">{{.Name}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{if .Scripts.Extra}}
|
||||
<h4>Extra Scripts</h4>
|
||||
<ul class="item-list">
|
||||
{{range .Scripts.Extra}}
|
||||
<li class="extra">{{.Name}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{if .Scripts.Modified}}
|
||||
<h4>Modified Scripts</h4>
|
||||
<ul class="item-list">
|
||||
{{range .Scripts.Modified}}
|
||||
<li class="modified">{{.Name}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -104,6 +104,26 @@ func TestFormatSummary(t *testing.T) {
|
||||
},
|
||||
wantStr: []string{"Tables:", "Missing: 1", "Extra: 1", "Modified: 1"},
|
||||
},
|
||||
{
|
||||
name: "with script differences",
|
||||
result: &DiffResult{
|
||||
Source: "source",
|
||||
Target: "target",
|
||||
Schemas: &SchemaDiff{
|
||||
Modified: []*SchemaChange{
|
||||
{
|
||||
Name: "public",
|
||||
Scripts: &ScriptDiff{
|
||||
Missing: []*models.Script{{Name: "create_users"}},
|
||||
Extra: []*models.Script{{Name: "seed_users"}},
|
||||
Modified: []*ScriptChange{{Name: "add_indexes"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStr: []string{"Scripts:", "Missing: 1", "Extra: 1", "Modified: 1"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -237,6 +257,31 @@ func TestFormatHTML(t *testing.T) {
|
||||
"text",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with script modifications",
|
||||
result: &DiffResult{
|
||||
Source: "source",
|
||||
Target: "target",
|
||||
Schemas: &SchemaDiff{
|
||||
Modified: []*SchemaChange{
|
||||
{
|
||||
Name: "public",
|
||||
Scripts: &ScriptDiff{
|
||||
Missing: []*models.Script{{Name: "create_users"}},
|
||||
Extra: []*models.Script{{Name: "seed_users"}},
|
||||
Modified: []*ScriptChange{{Name: "add_indexes"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStr: []string{
|
||||
"Scripts",
|
||||
"create_users",
|
||||
"seed_users",
|
||||
"add_indexes",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -22,6 +22,7 @@ type SchemaChange struct {
|
||||
Tables *TableDiff `json:"tables,omitempty"`
|
||||
Views *ViewDiff `json:"views,omitempty"`
|
||||
Sequences *SequenceDiff `json:"sequences,omitempty"`
|
||||
Scripts *ScriptDiff `json:"scripts,omitempty"`
|
||||
}
|
||||
|
||||
// TableDiff represents differences in tables
|
||||
@@ -131,6 +132,21 @@ type SequenceChange struct {
|
||||
Changes map[string]any `json:"changes"`
|
||||
}
|
||||
|
||||
// ScriptDiff represents differences in migration scripts.
|
||||
type ScriptDiff struct {
|
||||
Missing []*models.Script `json:"missing"` // Scripts in source but not in target
|
||||
Extra []*models.Script `json:"extra"` // Scripts in target but not in source
|
||||
Modified []*ScriptChange `json:"modified"` // Scripts that exist in both but differ
|
||||
}
|
||||
|
||||
// ScriptChange represents a modified migration script.
|
||||
type ScriptChange struct {
|
||||
Name string `json:"name"`
|
||||
Source *models.Script `json:"source"`
|
||||
Target *models.Script `json:"target"`
|
||||
Changes map[string]any `json:"changes"`
|
||||
}
|
||||
|
||||
// Summary provides counts for quick overview
|
||||
type Summary struct {
|
||||
Schemas SchemaSummary `json:"schemas"`
|
||||
@@ -141,6 +157,7 @@ type Summary struct {
|
||||
Relationships RelationshipSummary `json:"relationships"`
|
||||
Views ViewSummary `json:"views"`
|
||||
Sequences SequenceSummary `json:"sequences"`
|
||||
Scripts ScriptSummary `json:"scripts"`
|
||||
}
|
||||
|
||||
type SchemaSummary struct {
|
||||
@@ -190,3 +207,9 @@ type SequenceSummary struct {
|
||||
Extra int `json:"extra"`
|
||||
Modified int `json:"modified"`
|
||||
}
|
||||
|
||||
type ScriptSummary struct {
|
||||
Missing int `json:"missing"`
|
||||
Extra int `json:"extra"`
|
||||
Modified int `json:"modified"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user