6 Commits

Author SHA1 Message Date
8869ad6914
fix(sqlmigrate): address low-severity review findings
Core library:
- Replace O(n*m) isApplied linear scan with map lookups (appliedSets)
- Wrap exec errors with ErrExecFailed in Up and Down
- Add tests for Collect with subpath and empty subpath
- Add tests for Latest and Drop convenience wrappers
- Add tests for Applied() error propagation in Up/Down/GetStatus
- Add tests for ErrExecFailed wrapping
- Add tests for context propagation and cancellation
- Store received context in mock for propagation assertions

shmigrate:
- Return ErrExecFailed on missing migration file instead of writing
  a comment and continuing
- Use r.FS for file stat (consistent with Applied), falling back
  to os.Stat when FS is nil
2026-04-09 17:16:27 -06:00
13a15b14db
fix(sqlmigrate): address high-severity review findings
- Collect: replace WalkDir with ReadDir to avoid subdirectory recursion
  and the risk of duplicate filenames silently overwriting each other
- shmigrate: check scanner.Err() after reading migrations log
- shmigrate: shell-quote all paths in generated scripts to prevent
  shell injection via migration filenames
- shmigrate: wrap errors with ErrQueryApplied sentinel for consistency
  with DB backends
- mustInit: remove stale err check that could never fire
- mustInit: fix swapped error messages (read vs create)
2026-04-09 17:13:56 -06:00
dec11dd6d6
fix(shmigrate): explicit Close throwaway
- Replace bare `defer f.Close()` with `defer func() { _ = f.Close() }()`
  for explicit error throwaway (consistent with other backends)
2026-04-09 03:53:25 -06:00
75e4a883b7
ref(sqlmigrate): split Migration into Migration + Script
Migration{ID, Name} is the identity type returned by Up/Down/Latest/Drop.
Script{Migration, Up, Down} holds collected SQL content from Collect().

Migrator interface now takes SQL as a separate parameter:
  ExecUp(ctx, Migration, sql string)
  ExecDown(ctx, Migration, sql string)

This separates identity from content — callers that track what ran
don't need to carry around SQL strings they'll never use.

Updates shmigrate to match (ignores the sql parameter, references
files on disk instead).
2026-04-09 03:21:46 -06:00
9c672a9d76
fix(shmigrate): use errors.Is for fs.ErrNotExist compatibility
os.IsNotExist does not recognize fs.ErrNotExist when wrapped by an
fs.FS implementation. Switch to errors.Is(err, fs.ErrNotExist) so
the "file not found" check works for both os.Open and fs.FS.Open.
2026-04-08 17:52:53 -06:00
3547b7e409
ref(database/sqlmigrate): extract migration library with shmigrate backend
Factor the inline migration logic from cmd/sql-migrate into reusable
packages: database/sqlmigrate (core types, matching, file collection)
and database/sqlmigrate/shmigrate (shell script generation backend).

No behavior changes — the CLI produces identical output. The shmigrate
package implements the sqlmigrate.Migrator interface so other backends
(pgmigrate, mymigrate, etc.) can follow the same pattern.
2026-04-08 15:37:03 -06:00