@@ -30,6 +30,7 @@ import Common.Api
30import Common.Schema
30import Common.Schema
31import Backend.Schema
31import Backend.Schema
32import Common.Route
32import Common.Route
33import qualified Data.Map.Monoidal as Map
34import qualified Data.Map.Monoidal as Map
34import Data.Maybe
35import Data.Maybe
35import Data.Pool
36import Data.Pool
@@ -40,8 +41,6 @@ import Obelisk.Api
40import Obelisk.Db
41import Obelisk.Db
41import Obelisk.Postgres.LogicalDecoding.Plugins.TestDecoding
42import Obelisk.Postgres.LogicalDecoding.Plugins.TestDecoding
42import qualified Data.Map as OldMap
43import qualified Data.Map as OldMap
43−import Data.Sequence (Seq)
45import Data.Text.Encoding
44import Data.Text.Encoding
46import Data.HashMap.Strict (HashMap)
45import Data.HashMap.Strict (HashMap)
47import qualified Data.HashMap.Strict as HashMap
46import qualified Data.HashMap.Strict as HashMap
@@ -189,6 +188,8 @@ to' = Generic.to
189instance ( Generic (tbl (FieldDecoder be))
188instance ( Generic (tbl (FieldDecoder be))
190 , GPatchFields (Rep (tbl (FieldDecoder be)) ())
189 , GPatchFields (Rep (tbl (FieldDecoder be)) ())
191 , Table tbl
190 , Table tbl
191+ , Ord (PrimaryKey tbl Identity)
192 ) => PatchEntity be (TableEntity tbl) where
193 ) => PatchEntity be (TableEntity tbl) where
193 patchEntity = EntityPatchDecoder_Table $ to' gPatchFields
194 patchEntity = EntityPatchDecoder_Table $ to' gPatchFields
@@ -205,16 +206,16 @@ class PatchableEntity be e where
206data EntityPatchDecoder be e where
207data EntityPatchDecoder be e where
207 EntityPatchDecoder_NotDecodable :: EntityPatchDecoder be e
208 EntityPatchDecoder_NotDecodable :: EntityPatchDecoder be e
208− EntityPatchDecoder_Table :: Table tbl => tbl (FieldDecoder be) -> EntityPatchDecoder be (TableEntity tbl)
209+ EntityPatchDecoder_Table :: (Table tbl, Ord (PrimaryKey tbl Identity), Eq (tbl Maybe)) => tbl (FieldDecoder be) -> EntityPatchDecoder be (TableEntity tbl)
210data FieldDecoder be a where
211data FieldDecoder be a where
211 FieldDecoder :: BackendFromField be a => FieldDecoder be a
212 FieldDecoder :: BackendFromField be a => FieldDecoder be a
213data EntityPatch e where
214data EntityPatch e where
214 EntityPatch_NotPatchable :: EntityPatch e
215 EntityPatch_NotPatchable :: EntityPatch e
215− EntityPatch_Table :: Seq (These (Maybe (PrimaryKey tbl Identity)) (tbl Maybe)) -> EntityPatch (TableEntity tbl) --TODO: Add old keys / deletes
216+ EntityPatch_Table :: PatchMapWithMove2 (PrimaryKey tbl Identity) (PartialUpdate tbl) -> EntityPatch (TableEntity tbl)
217−deriving instance (Show (PrimaryKey tbl Identity), Show (tbl Maybe)) => Show (EntityPatch (TableEntity tbl))
218+deriving instance (Show (PrimaryKey tbl Identity), Show (tbl Maybe), Show (tbl Identity)) => Show (EntityPatch (TableEntity tbl))
219deriving instance FromField a => FromField (SqlSerial a)
220deriving instance FromField a => FromField (SqlSerial a)
@@ -259,26 +260,20 @@ sequenceMaybeBeamable t = zipBeamFieldsM (\(Columnar' a) _ -> Columnar' <$> a) t
260newtype PartialUpdate tbl = PartialUpdate (tbl Maybe)
261newtype PartialUpdate tbl = PartialUpdate (tbl Maybe)
263+instance Beamable tbl => Semigroup (PartialUpdate tbl) where
264+ PartialUpdate a <> PartialUpdate b = PartialUpdate $ runIdentity $ zipBeamFieldsM (\(Columnar' x) (Columnar' y) -> pure $ Columnar' $ x <|> y) a b
266+instance Beamable tbl => Monoid (PartialUpdate tbl) where
267+ mempty = PartialUpdate $ runIdentity $ zipBeamFieldsM (\_ _ -> pure $ Columnar' Nothing) tblSkeleton tblSkeleton
270+deriving instance Show (tbl Maybe) => Show (PartialUpdate tbl)
271+deriving instance Eq (tbl Maybe) => Eq (PartialUpdate tbl)
262instance Beamable tbl => Patch (PartialUpdate tbl) where
273instance Beamable tbl => Patch (PartialUpdate tbl) where
263 type PatchTarget (PartialUpdate tbl) = tbl Identity
274 type PatchTarget (PartialUpdate tbl) = tbl Identity
264 apply (PartialUpdate new) old = zipBeamFieldsM (\(Columnar' newField) (Columnar' oldField) -> pure $ Columnar' $ fromMaybe oldField newField) new old
275 apply (PartialUpdate new) old = zipBeamFieldsM (\(Columnar' newField) (Columnar' oldField) -> pure $ Columnar' $ fromMaybe oldField newField) new old
266−theseToPatch :: Table tbl => These (Maybe (PrimaryKey tbl Identity)) (tbl Maybe) -> PatchMapWithMove2 (PrimaryKey tbl Identity) (PartialUpdate tbl)
269− | Just r <- sequenceMaybeBeamable rm
270− -> patchMapWithMove2InsertAll $ OldMap.singleton (primaryKey r) r
271− | otherwise -> error "Incomplete insert"
272− This Nothing -> error "Incomplete delete"
273− This (Just k) -> PatchMapWithMove2 $ OldMap.singleton k $ NodeInfo
274− { _nodeInfo_from = From_Delete
275− , _nodeInfo_to = Nothing
277− These mk vm -> let k = fromMaybe (error "Update without key") $ mk <|> sequenceMaybeBeamable (primaryKey vm) in PatchMapWithMove2 $ OldMap.singleton k $ NodeInfo
278− { _nodeInfo_from = From_Move k $ PartialUpdate vm
279− , _nodeInfo_to = Nothing
282--TODO: Inserts should always be complete; deletes should always have a key
277--TODO: Inserts should always be complete; deletes should always have a key
283decodeChange
278decodeChange
284 :: forall tbl
279 :: forall tbl
@@ -287,7 +282,7 @@ decodeChange
287 -> tbl (TableField tbl)
282 -> tbl (TableField tbl)
288 -> tbl (FieldDecoder Postgres)
283 -> tbl (FieldDecoder Postgres)
289 -> Change
284 -> Change
290− -> IO (These (Maybe (PrimaryKey tbl Identity)) (tbl Maybe))
285+ -> IO (PatchMapWithMove2 (PrimaryKey tbl Identity) (PartialUpdate tbl))
291decodeChange conn settings decoders =
286decodeChange conn settings decoders =
292 let decodeRecord = decodeRow conn settings decoders
287 let decodeRecord = decodeRow conn settings decoders
293 decodeKey k = do
288 decodeKey k = do
@@ -296,13 +291,26 @@ decodeChange conn settings decoders =
296 Nothing -> fail "Got partial key"
291 Nothing -> fail "Got partial key"
297 Just a -> pure a
292 Just a -> pure a
299− Change_Insert r -> That
294+ Change_Insert rRaw -> do
300− <$> decodeRecord r
295+ rm <- decodeRecord rRaw
296+ case sequenceMaybeBeamable rm of
297+ Nothing -> fail "Insert got partial row"
298+ Just r -> pure $ patchMapWithMove2InsertAll $ OldMap.singleton (primaryKey r) r
301− Change_Update mk r -> These
299+ Change_Update mkRaw rRaw -> do
302− <$> traverse decodeKey mk
300+ mk <- traverse decodeKey mkRaw
303− <*> decodeRecord r
301+ vm <- decodeRecord rRaw
302+ case mk <|> sequenceMaybeBeamable (primaryKey vm) of
303+ Nothing -> fail "Update with no key"
304+ Just k -> pure $ PatchMapWithMove2 $ OldMap.singleton k $ NodeInfo
305+ { _nodeInfo_from = From_Move k $ PartialUpdate vm
306+ , _nodeInfo_to = Just k
304− Change_Delete k -> This . Just
308+ Change_Delete kRaw -> do
305− <$> decodeKey k
309+ k <- decodeKey kRaw
310+ pure $ PatchMapWithMove2 $ OldMap.singleton k $ NodeInfo
311+ { _nodeInfo_from = From_Delete
312+ , _nodeInfo_to = Nothing
307decodeTransaction :: Connection -> Transaction -> IO (Db EntityPatch)
315decodeTransaction :: Connection -> Transaction -> IO (Db EntityPatch)
308decodeTransaction conn (_, tableChanges) = zipTables (Proxy :: Proxy Postgres) f db (defaultDatabasePatchDecoder @Postgres @Db)
316decodeTransaction conn (_, tableChanges) = zipTables (Proxy :: Proxy Postgres) f db (defaultDatabasePatchDecoder @Postgres @Db)
@@ -315,5 +323,5 @@ decodeTransaction conn (_, tableChanges) = zipTables (Proxy :: Proxy Postgres) f
315 x :: IO (EntityPatch entityType)
323 x :: IO (EntityPatch entityType)
316 x = case decoder of
324 x = case decoder of
317 EntityPatchDecoder_NotDecodable -> pure EntityPatch_NotPatchable
325 EntityPatchDecoder_NotDecodable -> pure EntityPatch_NotPatchable
318− EntityPatchDecoder_Table decoders -> EntityPatch_Table <$> traverse (decodeChange conn (dbTableSettings descriptor) decoders) vals
326+ EntityPatchDecoder_Table decoders -> EntityPatch_Table . mconcat . toList <$> traverse (decodeChange conn (dbTableSettings descriptor) decoders) vals