diff --git a/src/Platforms/AbstractPlatform.php b/src/Platforms/AbstractPlatform.php index d79d31eb5f7..b84b7dc3850 100644 --- a/src/Platforms/AbstractPlatform.php +++ b/src/Platforms/AbstractPlatform.php @@ -1652,6 +1652,32 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey return $query; } + /** + * Returns the SQL fragment representing the deferrability of a constraint. + */ + protected function getConstraintDeferrabilitySQL(ForeignKeyConstraint $foreignKey): string + { + $sql = ''; + + if ($foreignKey->hasOption('deferrable')) { + if ($foreignKey->getOption('deferrable') !== false) { + $sql .= ' DEFERRABLE'; + } else { + $sql .= ' NOT DEFERRABLE'; + } + } + + if ($foreignKey->hasOption('deferred')) { + if ($foreignKey->getOption('deferred') !== false) { + $sql .= ' INITIALLY DEFERRED'; + } else { + $sql .= ' INITIALLY IMMEDIATE'; + } + } + + return $sql; + } + /** * Returns the given referential action in uppercase if valid, otherwise throws an exception. * diff --git a/src/Platforms/OraclePlatform.php b/src/Platforms/OraclePlatform.php index 4b3a0545c6e..ad125772240 100644 --- a/src/Platforms/OraclePlatform.php +++ b/src/Platforms/OraclePlatform.php @@ -483,17 +483,23 @@ public function getDropForeignKeySQL(string $foreignKey, string $table): string /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string { - $referentialAction = ''; + $sql = ''; if ($foreignKey->hasOption('onDelete')) { $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + + if ($referentialAction !== '') { + $sql .= ' ON DELETE ' . $referentialAction; + } } - if ($referentialAction !== '') { - return ' ON DELETE ' . $referentialAction; + $deferrabilitySQL = $this->getConstraintDeferrabilitySQL($foreignKey); + + if ($deferrabilitySQL !== '') { + $sql .= $deferrabilitySQL; } - return ''; + return $sql; } /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ diff --git a/src/Platforms/PostgreSQLPlatform.php b/src/Platforms/PostgreSQLPlatform.php index d543377cc3d..1baf79349f9 100644 --- a/src/Platforms/PostgreSQLPlatform.php +++ b/src/Platforms/PostgreSQLPlatform.php @@ -182,20 +182,10 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - if ($foreignKey->hasOption('deferrable')) { - if ($foreignKey->getOption('deferrable') !== false) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - } + $deferrabilitySQL = $this->getConstraintDeferrabilitySQL($foreignKey); - if ($foreignKey->hasOption('deferred')) { - if ($foreignKey->getOption('deferred') !== false) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } + if ($deferrabilitySQL !== '') { + $query .= $deferrabilitySQL; } return $query; diff --git a/src/Schema/OracleSchemaManager.php b/src/Schema/OracleSchemaManager.php index 8d1624fa186..5d345aca5aa 100644 --- a/src/Schema/OracleSchemaManager.php +++ b/src/Schema/OracleSchemaManager.php @@ -207,6 +207,8 @@ protected function _getPortableTableForeignKeysList(array $tableForeignKeys): ar 'foreign' => [], 'foreignTable' => $value['references_table'], 'onDelete' => $value['delete_rule'], + 'deferrable' => $value['deferrable'] === 'DEFERRABLE', + 'deferred' => $value['deferred'] === 'DEFERRED', ]; } @@ -230,7 +232,11 @@ protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey) $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), $tableForeignKey['foreign'], $this->getQuotedIdentifierName($tableForeignKey['name']), - ['onDelete' => $tableForeignKey['onDelete']], + [ + 'onDelete' => $tableForeignKey['onDelete'], + 'deferrable' => $tableForeignKey['deferrable'], + 'deferred' => $tableForeignKey['deferred'], + ], ); } @@ -412,6 +418,8 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= <<<'SQL' ALC.CONSTRAINT_NAME, ALC.DELETE_RULE, + ALC.DEFERRABLE, + ALC.DEFERRED, COLS.COLUMN_NAME LOCAL_COLUMN, COLS.POSITION, R_COLS.TABLE_NAME REFERENCES_TABLE, diff --git a/tests/Functional/Schema/ForeignKeyConstraintTest.php b/tests/Functional/Schema/ForeignKeyConstraintTest.php index aec7fe1789e..c4395701d09 100644 --- a/tests/Functional/Schema/ForeignKeyConstraintTest.php +++ b/tests/Functional/Schema/ForeignKeyConstraintTest.php @@ -311,7 +311,7 @@ public function testDeferrabilityIntrospection(array $options, array $expectedOp if ($platform instanceof SQLitePlatform) { self::markTestIncomplete('Not all combinations of options are currently properly introspected on SQLite.'); - } elseif (! $platform instanceof PostgreSQLPlatform) { + } elseif (! $platform instanceof PostgreSQLPlatform && ! $platform instanceof OraclePlatform) { self::markTestSkipped(sprintf( 'Introspection of constraint deferrability is currently unsupported on %s.', $platform::class,