@@ -11,7 +11,8 @@ import Database.Beam.Migrate.SQL.Types
1111import Database.Beam.Migrate.Serialization
1212import Database.Beam.Migrate.Types.Predicates
1313
14- import Data.Aeson ((.:) , (.=) , withObject , object )
14+ import Data.Aeson ((.:) , (.:?) , (.=) , withObject , object )
15+ import Data.Maybe (fromMaybe )
1516import Data.Aeson.Types (Parser , Value )
1617import Data.Hashable (Hashable (.. ))
1718import qualified Data.List.NonEmpty as NE (NonEmpty )
@@ -168,6 +169,67 @@ instance DatabasePredicate TableHasPrimaryKey where
168169 | Just (TableExistsPredicate tblNm') <- cast p' = tblNm' == tblNm
169170 | otherwise = False
170171
172+ -- | Asserts that the given table has a foreign key referencing another table.
173+ -- Create these predicates with
174+ -- 'Database.Beam.Migrate.Types.CheckedEntities.addTableForeignKey'.
175+ data TableHasForeignKey
176+ = TableHasForeignKey
177+ { hasForeignKey_table :: QualifiedName -- ^ source table
178+ , hasForeignKey_columns :: NE. NonEmpty Text -- ^ local columns (in order)
179+ , hasForeignKey_refTable :: QualifiedName -- ^ referenced table
180+ , hasForeignKey_refColumns :: NE. NonEmpty Text -- ^ referenced columns (in order)
181+ , hasForeignKey_onUpdate :: ForeignKeyAction
182+ , hasForeignKey_onDelete :: ForeignKeyAction
183+ } deriving (Show , Eq , Generic )
184+ instance Hashable TableHasForeignKey
185+ instance DatabasePredicate TableHasForeignKey where
186+ englishDescription (TableHasForeignKey { hasForeignKey_table = tbl
187+ , hasForeignKey_columns = cols
188+ , hasForeignKey_refTable = refTbl
189+ , hasForeignKey_refColumns = refCols
190+ , hasForeignKey_onUpdate = onUpd
191+ , hasForeignKey_onDelete = onDel }) =
192+ " Table " <> show tbl <> " has foreign key on " <> show cols <>
193+ " referencing " <> show refTbl <> " " <> show refCols <>
194+ " ON UPDATE " <> show onUpd <>
195+ " ON DELETE " <> show onDel
196+
197+ predicateSpecificity _ = PredicateSpecificityAllBackends
198+
199+ serializePredicate (TableHasForeignKey { hasForeignKey_table = tbl
200+ , hasForeignKey_columns = cols
201+ , hasForeignKey_refTable = refTbl
202+ , hasForeignKey_refColumns = refCols
203+ , hasForeignKey_onUpdate = onUpd
204+ , hasForeignKey_onDelete = onDel }) =
205+ object [ " has-foreign-key" .= object
206+ [ " table" .= tbl
207+ , " columns" .= cols
208+ , " ref-table" .= refTbl
209+ , " ref-columns" .= refCols
210+ , " on-update" .= serializeForeignKeyAction onUpd
211+ , " on-delete" .= serializeForeignKeyAction onDel
212+ ] ]
213+
214+ predicateCascadesDropOn (TableHasForeignKey { hasForeignKey_table = tblNm }) p'
215+ | Just (TableExistsPredicate tblNm') <- cast p' = tblNm' == tblNm
216+ | otherwise = False
217+
218+ serializeForeignKeyAction :: ForeignKeyAction -> Text
219+ serializeForeignKeyAction ForeignKeyActionCascade = " cascade"
220+ serializeForeignKeyAction ForeignKeyActionSetNull = " set-null"
221+ serializeForeignKeyAction ForeignKeyActionSetDefault = " set-default"
222+ serializeForeignKeyAction ForeignKeyActionRestrict = " restrict"
223+ serializeForeignKeyAction ForeignKeyNoAction = " no-action"
224+
225+ deserializeForeignKeyAction :: Text -> Maybe ForeignKeyAction
226+ deserializeForeignKeyAction " cascade" = Just ForeignKeyActionCascade
227+ deserializeForeignKeyAction " set-null" = Just ForeignKeyActionSetNull
228+ deserializeForeignKeyAction " set-default" = Just ForeignKeyActionSetDefault
229+ deserializeForeignKeyAction " restrict" = Just ForeignKeyActionRestrict
230+ deserializeForeignKeyAction " no-action" = Just ForeignKeyNoAction
231+ deserializeForeignKeyAction _ = Nothing
232+
171233-- * Deserialization
172234
173235-- | 'BeamDeserializers' for all the predicates defined in this module
@@ -182,6 +244,7 @@ beamCheckDeserializers = mconcat
182244 , beamDeserializer (const deserializeTableExistsPredicate)
183245 , beamDeserializer (const deserializeTableHasPrimaryKeyPredicate)
184246 , beamDeserializer (const deserializeTableHasIndexPredicate)
247+ , beamDeserializer (const deserializeTableHasForeignKeyPredicate)
185248 , beamDeserializer deserializeTableHasColumnPredicate
186249 , beamDeserializer deserializeTableColumnHasConstraintPredicate
187250 ]
@@ -214,6 +277,20 @@ beamCheckDeserializers = mconcat
214277 <*> v' .: " columns"
215278 <*> (deserializeIndexOptions @ (BeamSqlBackendSyntax be ) =<< v' .: " options" )))
216279
280+ deserializeTableHasForeignKeyPredicate :: Value -> Parser SomeDatabasePredicate
281+ deserializeTableHasForeignKeyPredicate =
282+ withObject " TableHasForeignKey" $ \ v ->
283+ v .: " has-foreign-key" >>=
284+ (withObject " TableHasForeignKey" $ \ v' ->
285+ SomeDatabasePredicate <$>
286+ (TableHasForeignKey
287+ <$> v' .: " table"
288+ <*> v' .: " columns"
289+ <*> v' .: " ref-table"
290+ <*> v' .: " ref-columns"
291+ <*> (fromMaybe ForeignKeyNoAction . (>>= deserializeForeignKeyAction) <$> v' .:? " on-update" )
292+ <*> (fromMaybe ForeignKeyNoAction . (>>= deserializeForeignKeyAction) <$> v' .:? " on-delete" )))
293+
217294 deserializeTableHasColumnPredicate :: BeamDeserializers be'
218295 -> Value -> Parser SomeDatabasePredicate
219296 deserializeTableHasColumnPredicate d =
0 commit comments