@@ -89,7 +89,7 @@ public function createMigrationTable():void {
8989 $ this ->dbClient ->executeSql (implode ("\n" , [
9090 "create table if not exists ` {$ this ->tableName }` ( " ,
9191 "` " . self ::COLUMN_QUERY_NUMBER . "` int primary key, " ,
92- "` " . self ::COLUMN_QUERY_HASH . "` varchar(32) not null, " ,
92+ "` " . self ::COLUMN_QUERY_HASH . "` varchar(32) null, " ,
9393 "` " . self ::COLUMN_MIGRATED_AT . "` datetime not null ) " ,
9494 ]));
9595 }
@@ -125,54 +125,62 @@ public function getMigrationFileList():array {
125125
126126 /** @param array<string> $fileList */
127127 public function checkFileListOrder (array $ fileList ):void {
128- $ counter = 0 ;
128+ $ previousNumber = null ;
129129 $ sequence = [];
130130
131131 foreach ($ fileList as $ file ) {
132- $ counter ++;
133132 $ migrationNumber = $ this ->extractNumberFromFilename ($ file );
134133 $ sequence []= $ migrationNumber ;
135134
136- if ($ counter !== $ migrationNumber ) {
137- throw new MigrationSequenceOrderException (
138- "Missing: $ counter "
139- );
135+ if (!is_null ($ previousNumber )) {
136+ if ($ migrationNumber === $ previousNumber ) {
137+ throw new MigrationSequenceOrderException ("Duplicate: $ migrationNumber " );
138+ }
139+ if ($ migrationNumber < $ previousNumber ) {
140+ throw new MigrationSequenceOrderException ("Out of order: $ migrationNumber before $ previousNumber " );
141+ }
140142 }
143+
144+ $ previousNumber = $ migrationNumber ;
141145 }
142146 }
143147
144148 /** @param array<string> $migrationFileList */
145149 public function checkIntegrity (
146150 array $ migrationFileList ,
147- ?int $ migrationCount = null
151+ ?int $ migrationStartFrom = null
148152 ):int {
149153 $ fileNumber = 0 ;
154+
155+ foreach ($ migrationFileList as $ file ) {
156+ $ fileNumber = $ this ->extractNumberFromFilename ($ file );
157+
158+ // If a start point is provided, skip files at or before that number
159+ // and only verify files AFTER the provided migration count.
160+ if (!is_null ($ migrationStartFrom ) && $ fileNumber <= $ migrationStartFrom ) {
161+ continue ;
162+ }
150163
151- foreach ($ migrationFileList as $ i => $ file ) {
152- $ fileNumber = $ i + 1 ;
153164 $ md5 = md5_file ($ file );
154165
155- if (is_null ($ migrationCount )
156- || $ fileNumber <= $ migrationCount ) {
157- $ result = $ this ->dbClient ->executeSql (implode ("\n" , [
158- "select ` " . self ::COLUMN_QUERY_HASH . "` " ,
159- "from ` {$ this ->tableName }` " ,
160- "where ` " . self ::COLUMN_QUERY_NUMBER . "` = ? " ,
161- "limit 1 " ,
162- ]), [$ fileNumber ]);
166+ $ result = $ this ->dbClient ->executeSql (implode ("\n" , [
167+ "select ` " . self ::COLUMN_QUERY_HASH . "` " ,
168+ "from ` {$ this ->tableName }` " ,
169+ "where ` " . self ::COLUMN_QUERY_NUMBER . "` = ? " ,
170+ "limit 1 " ,
171+ ]), [$ fileNumber ]);
163172
164- $ hashInDb = ($ result ->fetch ())->getString (self ::COLUMN_QUERY_HASH );
173+ $ hashInDb = ($ result ->fetch ())? ->getString(self ::COLUMN_QUERY_HASH );
165174
166- if ($ hashInDb !== $ md5 ) {
167- throw new MigrationIntegrityException ($ file );
168- }
175+ if ($ hashInDb && $ hashInDb !== $ md5 ) {
176+ throw new MigrationIntegrityException ($ file );
169177 }
170178 }
171179
172180 return $ fileNumber ;
173181 }
174182
175- protected function extractNumberFromFilename (string $ pathName ):int {
183+ public function extractNumberFromFilename (string $ pathName ):int {
176184 $ file = new SplFileInfo ($ pathName );
177185 $ filename = $ file ->getFilename ();
178186 preg_match ("/(\d+)-?.*\.sql/ " , $ filename , $ matches );
@@ -187,15 +195,15 @@ protected function extractNumberFromFilename(string $pathName):int {
187195 /** @param array<string> $migrationFileList */
188196 public function performMigration (
189197 array $ migrationFileList ,
190- int $ existingMigrationCount = 0
198+ int $ existingFileNumber = 0
191199 ):int {
192200 $ fileNumber = 0 ;
193201 $ numCompleted = 0 ;
202+
203+ foreach ($ migrationFileList as $ file ) {
204+ $ fileNumber = $ this ->extractNumberFromFilename ($ file );
194205
195- foreach ($ migrationFileList as $ i => $ file ) {
196- $ fileNumber = $ i + 1 ;
197-
198- if ($ fileNumber <= $ existingMigrationCount ) {
206+ if ($ fileNumber <= $ existingFileNumber ) {
199207 continue ;
200208 }
201209
@@ -282,7 +290,7 @@ public function selectSchema():void {
282290 }
283291 }
284292
285- protected function recordMigrationSuccess (int $ number , string $ hash ):void {
293+ protected function recordMigrationSuccess (int $ number , ? string $ hash ):void {
286294 $ now = "now() " ;
287295
288296 if ($ this ->driver === Settings::DRIVER_SQLITE ) {
@@ -300,6 +308,15 @@ protected function recordMigrationSuccess(int $number, string $hash):void {
300308 ]), [$ number , $ hash ]);
301309 }
302310
311+ /**
312+ * @param int $numberToForce A null-hashed migration will be marked as
313+ * successful with this number. This will allow the next number to be
314+ * executed out of sequence.
315+ */
316+ public function resetMigrationSequence (int $ numberToForce ):void {
317+ $ this ->recordMigrationSuccess ($ numberToForce , null );
318+ }
319+
303320 /**
304321 * @codeCoverageIgnore
305322 */
0 commit comments