5
5
-- | Defines common 'DatabasePredicate's that are shared among backends
6
6
module Database.Beam.Migrate.Checks where
7
7
8
+ import Control.Monad (guard )
8
9
import Database.Beam.Backend.SQL.SQL92
10
+
9
11
import Database.Beam.Migrate.Serialization
10
12
import Database.Beam.Migrate.SQL.SQL92
11
13
import Database.Beam.Migrate.SQL.Types
@@ -16,15 +18,18 @@ import Database.Beam.Schema.Tables
16
18
import Data.Aeson (object , withObject , (.:) , (.=) )
17
19
import Data.Aeson.Types (Parser , Value )
18
20
import Data.Hashable (Hashable (.. ))
21
+ import Data.Proxy (Proxy (.. ))
19
22
import Data.Text (Text )
20
- import Data.Typeable (Typeable , cast )
23
+ import Data.Typeable (Typeable , cast , splitTyConApp , typeOf , typeRep )
21
24
#if !MIN_VERSION_base(4, 11, 0)
22
25
import Data.Semigroup
23
26
#endif
24
27
25
28
import GHC.Exts (fromList , toList )
26
29
import GHC.Generics (Generic )
27
30
31
+ import Unsafe.Coerce (unsafeCoerce )
32
+
28
33
-- * Table checks
29
34
30
35
-- | Asserts that a table with the given name exists in a database
@@ -153,12 +158,25 @@ instance DatabasePredicate TableHasIndex where
153
158
| Just (TableExistsPredicate tblNm') <- cast p' = tblNm' == tblNm
154
159
| otherwise = False
155
160
156
- -- TODO: how to say that we cannot delete a column while an index on it exists?
157
- -- The following does not work since 'TableHasColumn' is polymorphic other used syntax, and
158
- -- we don't know which index to get here
159
- --
160
- -- predicateRestrictDropOf (TableHasIndex tblNm _) p'
161
- -- | Just (TableHasColumn tblNm' _ _) <- cast p' = tblNm' == tblNm
161
+ predicateRestrictsDropOf (TableHasIndex tblNm colNms _) p'
162
+ | Just (tblNm', colNm') <-
163
+ withTyCon (\ (TableHasColumn tblNm' colNm' _) -> (tblNm', colNm')) p' =
164
+ tblNm == tblNm' && (colNm' `elem` colNms)
165
+ | otherwise = False
166
+
167
+ -- | Match a given item's type against a type-level application with the given
168
+ -- type constructor. Applies the given function and returns 'Just' its result on match,
169
+ -- 'Nothing' otherwise.
170
+ -- Unlike 'cast', this function does not require @a@ type to be instance of 'Typeable'.
171
+ withTyCon
172
+ :: forall (con :: * -> * ) (item :: * ) r .
173
+ (Typeable con , Typeable item )
174
+ => (forall a . con a -> r ) -> item -> Maybe r
175
+ withTyCon f x = do
176
+ (itemTyCon, itemTyArgs@ (_ : _)) <- pure $ splitTyConApp (typeOf x)
177
+ (conTyCon, conTyArgs) <- pure $ splitTyConApp (typeRep (Proxy @ con ))
178
+ guard (itemTyCon == conTyCon && init itemTyArgs == conTyArgs)
179
+ return (f $ unsafeCoerce x)
162
180
163
181
-- | Convert gathered indices into checks.
164
182
entityIndicesToChecks
0 commit comments