1414use InvalidArgumentException ;
1515use PDO ;
1616use PDOException ;
17+ use Phinx \Db \Action \AddForeignKey ;
1718use Phinx \Db \Table \Column ;
1819use Phinx \Db \Table \ForeignKey ;
1920use Phinx \Db \Table \Index ;
@@ -1034,57 +1035,52 @@ protected function recreateIndicesAndTriggers(AlterInstructions $instructions):
10341035 * the given table, and of those tables whose constraints are
10351036 * targeting it.
10361037 *
1037- * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to process
1038- * @param string $tableName The name of the table for which to check constraints.
1039- * @return \Phinx\Db\Util\AlterInstructions
1038+ * @param string|array<string> $tableNames The name of the table for which to check constraints.
1039+ * @return void
10401040 */
1041- protected function validateForeignKeys (AlterInstructions $ instructions , string $ tableName ): AlterInstructions
1041+ protected function validateForeignKeys (string | array $ tableNames ): void
10421042 {
1043- $ instructions ->addPostStep (function ($ state ) use ($ tableName ) {
1044- $ tablesToCheck = [
1045- $ tableName ,
1046- ];
1047-
1048- $ otherTables = $ this
1049- ->query (
1050- "SELECT name FROM sqlite_master WHERE type = 'table' AND name != ? " ,
1051- [$ tableName ],
1052- )
1053- ->fetchAll ();
1054-
1055- foreach ($ otherTables as $ otherTable ) {
1056- $ foreignKeyList = $ this ->getTableInfo ($ otherTable ['name ' ], 'foreign_key_list ' );
1057- foreach ($ foreignKeyList as $ foreignKey ) {
1058- if (strcasecmp ($ foreignKey ['table ' ], $ tableName ) === 0 ) {
1059- $ tablesToCheck [] = $ otherTable ['name ' ];
1060- break ;
1061- }
1062- }
1063- }
1064-
1065- $ tablesToCheck = array_unique (array_map ('strtolower ' , $ tablesToCheck ));
1043+ if (!is_array ($ tableNames )) {
1044+ $ tableNames = [$ tableNames ];
1045+ }
10661046
1067- foreach ($ tablesToCheck as $ tableToCheck ) {
1068- $ schema = $ this ->getSchemaName ($ tableToCheck , true )['schema ' ];
1047+ $ tablesToCheck = $ tableNames ;
10691048
1070- $ stmt = $ this ->query (
1071- sprintf ('PRAGMA %sforeign_key_check(%s) ' , $ schema , $ this ->quoteTableName ($ tableToCheck )),
1072- );
1073- $ row = $ stmt ->fetch ();
1074- $ stmt ->closeCursor ();
1049+ $ otherTables = $ this
1050+ ->query (
1051+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT IN ( " . implode (', ' , array_fill (0 , count ($ tableNames ), '? ' )) . ') ' ,
1052+ $ tableNames ,
1053+ )
1054+ ->fetchAll ();
10751055
1076- if (is_array ($ row )) {
1077- throw new RuntimeException (sprintf (
1078- 'Integrity constraint violation: FOREIGN KEY constraint on `%s` failed. ' ,
1079- $ tableToCheck ,
1080- ));
1056+ foreach ($ otherTables as $ otherTable ) {
1057+ $ foreignKeyList = $ this ->getTableInfo ($ otherTable ['name ' ], 'foreign_key_list ' );
1058+ foreach ($ foreignKeyList as $ foreignKey ) {
1059+ if (in_array (strtolower ($ foreignKey ['table ' ]), $ tableNames )) {
1060+ $ tablesToCheck [] = $ otherTable ['name ' ];
1061+ break ;
10811062 }
10821063 }
1064+ }
10831065
1084- return $ state ;
1085- });
1066+ $ tablesToCheck = array_unique (array_map ('strtolower ' , $ tablesToCheck ));
10861067
1087- return $ instructions ;
1068+ foreach ($ tablesToCheck as $ tableToCheck ) {
1069+ $ schema = $ this ->getSchemaName ($ tableToCheck , true )['schema ' ];
1070+
1071+ $ stmt = $ this ->query (
1072+ sprintf ('PRAGMA %sforeign_key_check(%s) ' , $ schema , $ this ->quoteTableName ($ tableToCheck )),
1073+ );
1074+ $ row = $ stmt ->fetch ();
1075+ $ stmt ->closeCursor ();
1076+
1077+ if (is_array ($ row )) {
1078+ throw new RuntimeException (sprintf (
1079+ 'Integrity constraint violation: FOREIGN KEY constraint on `%s` failed. ' ,
1080+ $ tableToCheck ,
1081+ ));
1082+ }
1083+ }
10881084 }
10891085
10901086 /**
@@ -1230,16 +1226,13 @@ protected function beginAlterByCopyTable(string $tableName): AlterInstructions
12301226 * @param ?string $renamedOrRemovedColumnName The name of the renamed or removed column when part of a column
12311227 * rename/drop operation.
12321228 * @param ?string $newColumnName The new column name when part of a column rename operation.
1233- * @param bool $validateForeignKeys Whether to validate foreign keys after the copy and drop operations. Note that
1234- * enabling this option only has an effect when the `foreign_keys` PRAGMA is set to `ON`!
12351229 * @return \Phinx\Db\Util\AlterInstructions
12361230 */
12371231 protected function endAlterByCopyTable (
12381232 AlterInstructions $ instructions ,
12391233 string $ tableName ,
12401234 ?string $ renamedOrRemovedColumnName = null ,
12411235 ?string $ newColumnName = null ,
1242- bool $ validateForeignKeys = true ,
12431236 ): AlterInstructions {
12441237 $ instructions = $ this ->bufferIndicesAndTriggers ($ instructions , $ tableName );
12451238
@@ -1251,26 +1244,9 @@ protected function endAlterByCopyTable(
12511244 }
12521245 }
12531246
1254- $ foreignKeysEnabled = (bool )$ this ->fetchRow ('PRAGMA foreign_keys ' )['foreign_keys ' ];
1255-
1256- if ($ foreignKeysEnabled ) {
1257- $ instructions ->addPostStep ('PRAGMA foreign_keys = OFF ' );
1258- }
1259-
12601247 $ instructions = $ this ->copyAndDropTmpTable ($ instructions , $ tableName );
12611248 $ instructions = $ this ->recreateIndicesAndTriggers ($ instructions );
12621249
1263- if ($ foreignKeysEnabled ) {
1264- $ instructions ->addPostStep ('PRAGMA foreign_keys = ON ' );
1265- }
1266-
1267- if (
1268- $ foreignKeysEnabled &&
1269- $ validateForeignKeys
1270- ) {
1271- $ instructions = $ this ->validateForeignKeys ($ instructions , $ tableName );
1272- }
1273-
12741250 return $ instructions ;
12751251 }
12761252
@@ -1661,7 +1637,7 @@ protected function getDropPrimaryKeyInstructions(Table $table, string $column):
16611637 return $ newState + $ state ;
16621638 });
16631639
1664- return $ this ->endAlterByCopyTable ($ instructions , $ tableName , null , null , false );
1640+ return $ this ->endAlterByCopyTable ($ instructions , $ tableName , null , null );
16651641 }
16661642
16671643 /**
@@ -1673,7 +1649,6 @@ protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreig
16731649
16741650 $ tableName = $ table ->getName ();
16751651 $ instructions ->addPostStep (function ($ state ) use ($ foreignKey , $ tableName ) {
1676- $ this ->execute ('pragma foreign_keys = ON ' );
16771652 $ sql = substr ($ state ['createSQL ' ], 0 , -1 ) . ', ' . $ this ->getForeignKeySqlDefinition ($ foreignKey ) . '); ' ;
16781653
16791654 //Delete indexes from original table and recreate them in temporary table
@@ -2013,4 +1988,44 @@ public function getDecoratedConnection(): Connection
20131988
20141989 return $ this ->decoratedConnection = $ this ->buildConnection (SqliteDriver::class, $ options );
20151990 }
1991+
1992+ /**
1993+ * {@inheritDoc}
1994+ */
1995+ public function preExecuteActions (array $ updateSequences ): array
1996+ {
1997+ $ foreignKeysEnabled = (bool )$ this ->fetchRow ('PRAGMA foreign_keys ' )['foreign_keys ' ];
1998+
1999+ if (!$ foreignKeysEnabled ) {
2000+ foreach ($ updateSequences as $ updates ) {
2001+ foreach ($ updates as $ update ) {
2002+ foreach ($ update ->getActions () as $ action ) {
2003+ if ($ action instanceof AddForeignKey) {
2004+ $ foreignKeysEnabled = true ;
2005+ break 3 ;
2006+ }
2007+ }
2008+ }
2009+ }
2010+ }
2011+
2012+ if ($ foreignKeysEnabled ) {
2013+ $ this ->execute ('PRAGMA foreign_keys = OFF ' );
2014+ }
2015+
2016+ return [
2017+ 'foreignKeysEnabled ' => $ foreignKeysEnabled ,
2018+ ];
2019+ }
2020+
2021+ /**
2022+ * {@inheritDoc}
2023+ */
2024+ public function postExecuteActions (array $ tableNames , array $ preOptions ): void
2025+ {
2026+ if ($ preOptions ['foreignKeysEnabled ' ]) {
2027+ $ this ->execute ('PRAGMA foreign_keys = ON ' );
2028+ $ this ->validateForeignKeys ($ tableNames );
2029+ }
2030+ }
20162031}
0 commit comments