1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module UpsertWhere where
import PgInit
import Data.Time
import Database.Persist.Postgresql
share [mkPersist sqlSettings, mkMigrate "upsertWhereMigrate"] [persistLowerCase|
Item
name Text sqltype=varchar(80)
description Text
price Double Maybe
quantity Int Maybe
UniqueName name
deriving Eq Show Ord
ItemMigOnly
name Text
price Double
quantity Int
UniqueNameMigOnly name
createdAt UTCTime MigrationOnly default=CURRENT_TIMESTAMP
|]
wipe :: IO ()
wipe = runConnAssert $ do
deleteWhere ([] :: [Filter Item])
deleteWhere ([] :: [Filter ItemMigOnly])
itDb :: String -> SqlPersistT (LoggingT (ResourceT IO)) a -> SpecWith (Arg (IO ()))
itDb msg action = it msg $ runConnAssert $ void action
specs :: Spec
specs = describe "UpsertWhere" $ do
let item1 = Item "item1" "" (Just 3) Nothing
item2 = Item "item2" "hello world" Nothing (Just 2)
items = [item1, item2]
describe "upsertWhere" $ before_ wipe $ do
itDb "inserts appropriately" $ do
upsertWhere item1 [ItemDescription =. "i am item 1"] []
Just item <- fmap entityVal <$> getBy (UniqueName "item1")
item `shouldBe` item1
itDb "performs only updates given if record already exists" $ do
let newDescription = "I am a new description"
insert_ item1
upsertWhere
(Item "item1" "i am an inserted description" (Just 1) (Just 2))
[ItemDescription =. newDescription]
[]
Just item <- fmap entityVal <$> getBy (UniqueName "item1")
item `shouldBe` item1 { itemDescription = newDescription }
itDb "inserts with MigrationOnly fields (#1330)" $ do
upsertWhere
(ItemMigOnly "foobar" 20 1)
[ItemMigOnlyPrice +=. 2]
[]
describe "upsertManyWhere" $ do
itDb "inserts fresh records" $ do
insertMany_ items
let newItem = Item "item3" "fresh" Nothing Nothing
upsertManyWhere
(newItem : items)
[copyField ItemDescription]
[]
[]
dbItems <- map entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
itDb "updates existing records" $ do
let
postUpdate =
map (\i -> i { itemQuantity = fmap (+1) (itemQuantity i) }) items
insertMany_ items
upsertManyWhere
items
[]
[ItemQuantity +=. Just 1]
[]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` postUpdate
itDb "only copies passing values" $ do
insertMany_ items
let newItems = map (\i -> i { itemQuantity = Just 0, itemPrice = fmap (*2) (itemPrice i) }) items
postUpdate = map (\i -> i { itemPrice = fmap (*2) (itemPrice i) }) items
upsertManyWhere
newItems
[ copyUnlessEq ItemQuantity (Just 0)
, copyField ItemPrice
]
[]
[]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` postUpdate
itDb "inserts without modifying existing records if no updates specified" $ do
let newItem = Item "item3" "hi friends!" Nothing Nothing
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[]
[]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
itDb "inserts without modifying existing records if no updates specified and there's a filter with True condition" $
do
let newItem = Item "item3" "hi friends!" Nothing Nothing
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[]
[ItemDescription ==. "hi friends!"]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
itDb "inserts without updating existing records if there are updates specified but there's a filter with a False condition" $
do
let newItem = Item "item3" "hi friends!" Nothing Nothing
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[ItemQuantity +=. Just 1]
[ItemDescription ==. "hi friends!"]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
itDb "inserts new records but does not update existing records if there are updates specified but the modification condition is False" $
do
let newItem = Item "item3" "hi friends!" Nothing Nothing
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[ItemQuantity +=. Just 1]
[excludeNotEqualToOriginal ItemDescription]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
itDb "inserts new records and updates existing records if there are updates specified and the modification condition is True (because it's empty)" $
do
let newItem = Item "item3" "hello world" Nothing Nothing
postUpdate = map (\i -> i {itemQuantity = fmap (+ 1) (itemQuantity i)}) items
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[ItemQuantity +=. Just 1]
[]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : postUpdate)
itDb "inserts new records and updates existing records if there are updates specified and the modification filter condition is triggered" $
do
let newItem = Item "item3" "hi friends!" Nothing Nothing
postUpdate = map (\i -> i {itemQuantity = fmap (+1) (itemQuantity i)}) items
insertMany_ items
upsertManyWhere
(newItem : items)
[
copyUnlessEq ItemDescription "hi friends!"
, copyField ItemPrice
]
[ItemQuantity +=. Just 1]
[ItemDescription !=. "bye friends!"]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : postUpdate)
itDb "inserts an item and doesn't apply the update if the filter condition is triggered" $
do
let newItem = Item "item3" "hello world" Nothing Nothing
insertMany_ items
upsertManyWhere
(newItem : items)
[]
[ItemQuantity +=. Just 1]
[excludeNotEqualToOriginal ItemDescription]
dbItems <- fmap entityVal <$> selectList [] []
dbItems `shouldMatchList` (newItem : items)
|