Update idFromInsert regex to match schema-qualified table references
such as INSERT INTO authz._migrations, in addition to the existing
unqualified INSERT INTO _migrations form.
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).
API changes for v1:
- Collect(fsys, subpath) takes a subdirectory path (use "." for root),
enabling embed.FS with //go:embed sql/migrations/*.sql
- Latest() applies all pending migrations (shorthand for Up with n=-1)
- Drop() rolls back all applied migrations (shorthand for Down with n=-1)
Passing 0 to Up() or Down() is an easy mistake — it silently means
"all" which could be destructive. Now n=0 returns ErrInvalidN.
Convention: n > 0 for a specific count, n < 0 (typically -1) for all.
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.