@@ -13,20 +13,24 @@ import (
1313 "os"
1414)
1515
16+ // FileMigration implements the Migration interface. It helps to apply migrations from a file or fs.FS.
1617type FileMigration struct {
1718 Name string
1819 FS fs.FS
1920 migrationFunc func (tx * sql.Tx , migration string ) error
2021}
2122
23+ // Key returns the key of the migration to register in the migration table.
2224func (f FileMigration ) Key () string {
2325 return f .Name
2426}
2527
28+ // Migrate executes the migration on the given transaction.
2629func (f FileMigration ) Migrate (tx * sql.Tx ) error {
2730 return f .migrationFunc (tx , f .Name )
2831}
2932
33+ // WithMigrationFromFile generates a FileMigration that will run the content of the given file.
3034func WithMigrationFromFile (name string ) MorphOption {
3135 return func (morpher * Morpher ) error {
3236 morpher .Migrations = append (morpher .Migrations , FileMigration {
@@ -40,14 +44,16 @@ func WithMigrationFromFile(name string) MorphOption {
4044
4145 defer func () { _ = m .Close () }()
4246
43- return applyFileSteps (tx , m , migration , morpher .Log )
47+ return applyStepsStream (tx , m , migration , morpher .Log )
4448 },
4549 })
4650
4751 return nil
4852 }
4953}
5054
55+ // WithMigrationFromFileFS generates a FileMigration that will run the content of the given file from the
56+ // given filesystem.
5157func WithMigrationFromFileFS (name string , dir fs.FS ) MorphOption {
5258 return func (morpher * Morpher ) error {
5359 morpher .Migrations = append (morpher .Migrations , migrationFromFileFS (name , dir , morpher .Log ))
@@ -56,26 +62,27 @@ func WithMigrationFromFileFS(name string, dir fs.FS) MorphOption {
5662 }
5763}
5864
65+ // WithMigrationsFromFS generates a FileMigration that will run all migration scripts of the files in the given
66+ // filesystem.
5967func WithMigrationsFromFS (d fs.ReadDirFS ) MorphOption {
6068 return func (morpher * Morpher ) error {
6169 dirEntry , err := d .ReadDir ("." )
6270
63- if err != nil {
64- return err
65- }
66-
67- for _ , entry := range dirEntry {
68- morpher .Log .Info ("entry" , slog .String ("name" , entry .Name ()))
69- if entry .Type ().IsRegular () {
70- morpher .Migrations = append (morpher .Migrations ,
71- migrationFromFileFS (entry .Name (), d , morpher .Log ))
71+ if err == nil {
72+ for _ , entry := range dirEntry {
73+ morpher .Log .Info ("entry" , slog .String ("name" , entry .Name ()))
74+ if entry .Type ().IsRegular () {
75+ morpher .Migrations = append (morpher .Migrations ,
76+ migrationFromFileFS (entry .Name (), d , morpher .Log ))
77+ }
7278 }
7379 }
7480
75- return nil
81+ return err
7682 }
7783}
7884
85+ // migrationFromFileFS creates a FileMigration instance for a specific migration file from an fs.FS directory.
7986func migrationFromFileFS (name string , dir fs.FS , log * slog.Logger ) FileMigration {
8087 return FileMigration {
8188 Name : name ,
@@ -89,17 +96,20 @@ func migrationFromFileFS(name string, dir fs.FS, log *slog.Logger) FileMigration
8996
9097 defer func () { _ = m .Close () }()
9198
92- return applyFileSteps (tx , m , migration , log )
99+ return applyStepsStream (tx , m , migration , log )
93100 },
94101 }
95102}
96103
97- func applyFileSteps (tx * sql.Tx , r io.Reader , id string , log * slog.Logger ) error {
104+ // applyStepsStream executes database migration steps read from an io.Reader, separated by semicolons, in a transaction.
105+ // Returns the corresponding error if any step execution fails.
106+ func applyStepsStream (tx * sql.Tx , r io.Reader , id string , log * slog.Logger ) error {
98107 buf := bytes.Buffer {}
99108
100109 scanner := bufio .NewScanner (r )
110+ var i int
101111
102- for i : = 0 ; scanner .Scan (); {
112+ for i = 0 ; scanner .Scan (); {
103113 buf .Write (scanner .Bytes ())
104114
105115 if scanner .Text () == ";" {
@@ -116,5 +126,17 @@ func applyFileSteps(tx *sql.Tx, r io.Reader, id string, log *slog.Logger) error
116126 }
117127 }
118128
129+ // cleanup after, for final statement without the closing ; on a new line
130+ if buf .Len () > 0 {
131+ log .Info ("migration step" ,
132+ slog .String ("id" , id ),
133+ slog .Int ("step" , i ),
134+ )
135+
136+ if _ , err := tx .Exec (buf .String ()); err != nil {
137+ return err
138+ }
139+ }
140+
119141 return scanner .Err ()
120142}
0 commit comments