@@ -105,33 +105,10 @@ func verifyMigrations(tx *sqlx.Tx, migrations []NamedMigration) (firstUnappliedM
105105 return firstUnappliedMigrationIndex , nil
106106}
107107
108- func doMigrations (tx * sqlx.Tx , migrations []NamedMigration , startIndex int ) error {
109- for index := startIndex ; index < len (migrations ); index ++ {
110- migration := migrations [index ]
111- log .Printf ("Performing migration %d (%q)" , index , migration .Name )
112- err := migrations [index ].Migration .DoMigration (tx )
113- if err != nil {
114- return fmt .Errorf ("Error performing migration %d (%q): %w" , index , migration .Name , err )
115- }
116- _ , err = tx .Exec (`INSERT INTO migration ("index", name) VALUES ($1, $2)` , index , migration .Name )
117- if err != nil {
118- return fmt .Errorf ("Error recording migration %d (%q): %w" , index , migration .Name , err )
119- }
120- }
121-
122- return nil
123- }
124-
125- // Rollback runs the Reverse migrations for all the input migrations with index >= rollBackThroughIndex.
126- // The input migrations must include all migrations, not just the ones to roll back.
127- func Rollback (db * sqlx.DB , migrations []NamedMigration , rollBackThroughIndex int ) error {
128- err := ensureMigrationsTableExists (db )
129- if err != nil {
130- return err
131- }
108+ func migrateOne (db * sqlx.DB , migrations []NamedMigration ) (bool , error ) {
132109 tx , err := db .Beginx ()
133110 if err != nil {
134- return fmt .Errorf ("Error starting migrations transaction: %w" , err )
111+ return false , fmt .Errorf ("Error starting migrations transaction: %w" , err )
135112 }
136113 committed := false
137114 defer func () {
@@ -145,57 +122,40 @@ func Rollback(db *sqlx.DB, migrations []NamedMigration, rollBackThroughIndex int
145122
146123 _ , err = tx .Exec ("LOCK TABLE migration" )
147124 if err != nil {
148- return fmt .Errorf ("Error locking migration table: %w" , err )
125+ return false , fmt .Errorf ("Error locking migration table: %w" , err )
149126 }
150127
151128 firstUnappliedIndex , err := verifyMigrations (tx , migrations )
152129 if err != nil {
153- return err
130+ return false , err
154131 }
155-
156- if rollBackThroughIndex < 0 {
157- return fmt .Errorf ("Invalid target index %d" , rollBackThroughIndex )
158- }
159- if rollBackThroughIndex >= firstUnappliedIndex {
160- return fmt .Errorf ("Migration %d has not been applied yet" , rollBackThroughIndex )
132+ if firstUnappliedIndex >= len (migrations ) {
133+ return false , nil
161134 }
162135
163- for index := firstUnappliedIndex - 1 ; index >= rollBackThroughIndex ; index -- {
164- migration := migrations [index ]
165- if migration .Reverse == nil {
166- return fmt .Errorf ("No Reverse for migration %d (%q)" , index , migration .Name )
167- }
168- log .Printf ("Reversing migration %d (%q)" , index , migration .Name )
169- err := migrations [index ].Reverse .DoMigration (tx )
170- if err != nil {
171- return fmt .Errorf ("Error reversing migration %d (%q): %w" , index , migration .Name , err )
172- }
173- _ , err = tx .Exec (`DELETE FROM migration WHERE "index"=$1` , index )
174- if err != nil {
175- return fmt .Errorf ("Error deleting migration row %d (%q): %w" , index , migration .Name , err )
176- }
136+ migration := migrations [firstUnappliedIndex ]
137+ log .Printf ("Performing migration %d (%q)" , firstUnappliedIndex , migration .Name )
138+ err = migrations [firstUnappliedIndex ].Migration .DoMigration (tx )
139+ if err != nil {
140+ return false , fmt .Errorf ("Error performing migration %d (%q): %w" , firstUnappliedIndex , migration .Name , err )
141+ }
142+ _ , err = tx .Exec (`INSERT INTO migration ("index", name) VALUES ($1, $2)` , firstUnappliedIndex , migration .Name )
143+ if err != nil {
144+ return false , fmt .Errorf ("Error recording migration %d (%q): %w" , firstUnappliedIndex , migration .Name , err )
177145 }
178146
147+ committed = true
179148 err = tx .Commit ()
180149 if err != nil {
181- return fmt .Errorf ("Error committing migrations: %w" , err )
150+ return false , fmt .Errorf ("Error committing migrations: %w" , err )
182151 }
183- committed = true
184- return nil
152+ return true , nil
185153}
186154
187- // Migrate does the following:
188- // 1. Verifies that the `migration` table exists, and creates it if it does not.
189- // 2. Verifies that the existing migrations recorded in the database match (by name and order) the migrations given as the argument.
190- // 3. Performs any migrations that are not yet recorded in the database.
191- func Migrate (db * sqlx.DB , migrations []NamedMigration ) error {
192- err := ensureMigrationsTableExists (db )
193- if err != nil {
194- return err
195- }
155+ func rollbackOne (db * sqlx.DB , migrations []NamedMigration , rollBackThroughIndex int ) (rolledBackIndex int , err error ) {
196156 tx , err := db .Beginx ()
197157 if err != nil {
198- return fmt .Errorf ("Error starting migrations transaction: %w" , err )
158+ return - 1 , fmt .Errorf ("Error starting migrations transaction: %w" , err )
199159 }
200160 committed := false
201161 defer func () {
@@ -209,23 +169,78 @@ func Migrate(db *sqlx.DB, migrations []NamedMigration) error {
209169
210170 _ , err = tx .Exec ("LOCK TABLE migration" )
211171 if err != nil {
212- return fmt .Errorf ("Error locking migration table: %w" , err )
172+ return - 1 , fmt .Errorf ("Error locking migration table: %w" , err )
213173 }
214174
215175 firstUnappliedIndex , err := verifyMigrations (tx , migrations )
216176 if err != nil {
217- return err
177+ return - 1 , err
218178 }
219179
220- err = doMigrations (tx , migrations , firstUnappliedIndex )
180+ if rollBackThroughIndex < 0 {
181+ return - 1 , fmt .Errorf ("Invalid target index %d" , rollBackThroughIndex )
182+ }
183+ if rollBackThroughIndex >= firstUnappliedIndex {
184+ return - 1 , fmt .Errorf ("Migration %d has not been applied yet" , rollBackThroughIndex )
185+ }
186+
187+ index := firstUnappliedIndex - 1
188+ migration := migrations [index ]
189+ if migration .Reverse == nil {
190+ return - 1 , fmt .Errorf ("No Reverse for migration %d (%q)" , index , migration .Name )
191+ }
192+ log .Printf ("Reversing migration %d (%q)" , index , migration .Name )
193+ err = migrations [index ].Reverse .DoMigration (tx )
221194 if err != nil {
222- return err
195+ return - 1 , fmt .Errorf ("Error reversing migration %d (%q): %w" , index , migration .Name , err )
196+ }
197+ _ , err = tx .Exec (`DELETE FROM migration WHERE "index"=$1` , index )
198+ if err != nil {
199+ return - 1 , fmt .Errorf ("Error deleting migration row %d (%q): %w" , index , migration .Name , err )
223200 }
224201
202+ committed = true
225203 err = tx .Commit ()
226204 if err != nil {
227- return fmt .Errorf ("Error committing migrations: %w" , err )
205+ return - 1 , fmt .Errorf ("Error committing migrations: %w" , err )
206+ }
207+ return index , nil
208+ }
209+
210+ // Rollback runs the Reverse migrations for all the input migrations with index >= rollBackThroughIndex.
211+ // The input migrations must include all migrations, not just the ones to roll back.
212+ func Rollback (db * sqlx.DB , migrations []NamedMigration , rollBackThroughIndex int ) error {
213+ err := ensureMigrationsTableExists (db )
214+ if err != nil {
215+ return err
216+ }
217+ for {
218+ rolledBackIndex , err := rollbackOne (db , migrations , rollBackThroughIndex )
219+ if err != nil {
220+ return err
221+ }
222+ if rolledBackIndex == rollBackThroughIndex {
223+ return nil
224+ }
225+ }
226+ }
227+
228+ // Migrate does the following:
229+ // 1. Verifies that the `migration` table exists, and creates it if it does not.
230+ // 2. Verifies that the existing migrations recorded in the database match (by name and order) the migrations given as the argument.
231+ // 3. Performs any migrations that are not yet recorded in the database.
232+ func Migrate (db * sqlx.DB , migrations []NamedMigration ) error {
233+ err := ensureMigrationsTableExists (db )
234+ if err != nil {
235+ return err
236+ }
237+ for {
238+ migrated , err := migrateOne (db , migrations )
239+ if err != nil {
240+ return err
241+ }
242+ if ! migrated {
243+ return nil
244+ }
228245 }
229- committed = true
230- return nil
231246}
0 commit comments