captureDownSQL scans migration files on disk and stores both up and down SQL content in the tracking table for versions <= dbVersion.
(ctx context.Context, db *sql.DB, migrDir string, dbVersion int)
| 380 | // up and down SQL content in the tracking table for versions |
| 381 | // <= dbVersion. |
| 382 | func captureDownSQL(ctx context.Context, db *sql.DB, migrDir string, dbVersion int) error { |
| 383 | entries, err := os.ReadDir(migrDir) |
| 384 | if err != nil { |
| 385 | return xerrors.Errorf("read migrations dir: %w", err) |
| 386 | } |
| 387 | |
| 388 | for _, e := range entries { |
| 389 | name := e.Name() |
| 390 | if !strings.HasSuffix(name, ".down.sql") || len(name) < 7 { |
| 391 | continue |
| 392 | } |
| 393 | version, err := strconv.Atoi(name[:6]) |
| 394 | if err != nil || version > dbVersion { |
| 395 | continue |
| 396 | } |
| 397 | |
| 398 | downContent, err := os.ReadFile(filepath.Join(migrDir, name)) |
| 399 | if err != nil { |
| 400 | return xerrors.Errorf("read %s: %w", name, err) |
| 401 | } |
| 402 | |
| 403 | upName := strings.Replace(name, ".down.sql", ".up.sql", 1) |
| 404 | upContent, err := os.ReadFile(filepath.Join(migrDir, upName)) |
| 405 | if err != nil { |
| 406 | // Up file might not exist for some migrations. |
| 407 | upContent = nil |
| 408 | } |
| 409 | |
| 410 | _, err = db.ExecContext(ctx, ` |
| 411 | INSERT INTO _develop.applied_migrations (version, filename, up_sql, down_sql) |
| 412 | VALUES ($1, $2, $3, $4) |
| 413 | ON CONFLICT (version) DO UPDATE |
| 414 | SET filename = EXCLUDED.filename, up_sql = EXCLUDED.up_sql, down_sql = EXCLUDED.down_sql |
| 415 | `, version, name, string(upContent), string(downContent)) |
| 416 | if err != nil { |
| 417 | return xerrors.Errorf("upsert version %d: %w", version, err) |
| 418 | } |
| 419 | } |
| 420 | return nil |
| 421 | } |
| 422 | |
| 423 | // formatDiff produces a simple line-based diff between two strings. |
| 424 | func formatDiff(labelA, labelB, a, b string) string { |