fix: writer/reader correctness and determinism issues

- merge: cloneTable keeps relationships; skip-tables applies to new schemas
- diff: detect schema description/owner changes
- prisma: reader no longer turns relation fields into columns, enum
  detection via declared names; writer type mapping is ordered
- typeorm writer: keep explicit SQL types that cannot be inferred
- drizzle: enum columns call the enum constant; reader resolves them
- mysql/mssql/sqlite writers: honour OutputPath, deterministic column
  and constraint order; mssql live execute covers full schema
- template: ToYAML recovers from panics; Merge nil-pointer loop
- regenerate drizzle fixtures
This commit is contained in:
2026-10-03 21:33:59 +02:00
parent 08e1417393
commit a32647ee16
15 changed files with 301 additions and 180 deletions
+37 -18
View File
@@ -44,29 +44,37 @@ func (w *Writer) WriteDatabase(db *models.Database) error {
return w.executeDatabaseSQL(db, dbPath)
}
var writer io.Writer
var file *os.File
var err error
// Use existing writer if already set (for testing)
if w.writer != nil {
writer = w.writer
} else if w.options.OutputPath != "" {
// Determine output destination
file, err = os.Create(w.options.OutputPath)
if err != nil {
return fmt.Errorf("failed to create output file: %w", err)
}
defer file.Close()
writer = file
} else {
writer = os.Stdout
release, err := w.openOutput()
if err != nil {
return err
}
defer release()
w.writer = writer
return w.writeContent(db)
}
// openOutput points w.writer at the configured destination (output file or
// stdout) when none is set, and returns a func that releases it again so the
// writer can be reused.
func (w *Writer) openOutput() (func(), error) {
if w.writer != nil {
return func() {}, nil
}
if w.options.OutputPath != "" {
file, err := os.Create(w.options.OutputPath)
if err != nil {
return nil, fmt.Errorf("failed to create output file: %w", err)
}
w.writer = file
return func() {
file.Close()
w.writer = nil
}, nil
}
w.writer = os.Stdout
return func() { w.writer = nil }, nil
}
// writeContent writes the header, pragma, and every schema's DDL to w.writer.
func (w *Writer) writeContent(db *models.Database) error {
// Write header comment
@@ -184,6 +192,12 @@ func tableSchemaName(schema string) string {
// WriteSchema writes a single schema as SQLite SQL
func (w *Writer) WriteSchema(schema *models.Schema) error {
release, err := w.openOutput()
if err != nil {
return err
}
defer release()
tableSchema := tableSchemaName(schema.Name)
if err := w.checkDirectives(schema); err != nil {
@@ -229,6 +243,11 @@ func (w *Writer) WriteSchema(schema *models.Schema) error {
// WriteTable writes a single table as SQLite SQL
func (w *Writer) WriteTable(table *models.Table) error {
release, err := w.openOutput()
if err != nil {
return err
}
defer release()
return w.writeTable("", table)
}