Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 54 additions & 10 deletions src/Database/PostgreSQL/Entity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module Database.PostgreSQL.Entity
-- ** Update
, update
, updateFieldsBy
, updateFieldsWhere

-- ** Deletion
, delete
Expand Down Expand Up @@ -73,6 +74,7 @@ module Database.PostgreSQL.Entity
, _updateBy
, _updateFields
, _updateFieldsBy
, _updateFieldsWhere

-- ** Deletion
, _delete
Expand Down Expand Up @@ -361,6 +363,28 @@ updateFieldsBy
-> DBT m Int64
updateFieldsBy fs (f, oldValue) newValue = execute (_updateFieldsBy @e fs f) (toRow newValue ++ toRow (Only oldValue))

{-| Update rows of an entity matching the given values

== Example

> let newName = "Tiberus McElroy" :: Text
> let oldName = "Johnson McElroy" :: Text
> updateFieldsWhere @Author [[field| name |]] [[field| name |]] (newName,oldName)

@since TODO
-}
updateFieldsWhere
:: forall e values m
. (Entity e, MonadIO m, ToRow values)
=> Vector Field
-- ^ Fields to change
-> Vector Field
-- ^ Field on which to match and its value

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- ^ Field on which to match and its value
-- ^ Fields on which to match and their values

-> values
-- ^ Values of those fields
-> DBT m Int64
updateFieldsWhere fs f values = execute (_updateFieldsWhere @e fs f) values

{-| Delete an entity according to its primary key.

@since 0.0.1.0
Expand Down Expand Up @@ -683,25 +707,25 @@ _updateBy f = _updateFieldsBy @e (fields @e) f
_updateFields :: forall e. Entity e => Vector Field -> Query
_updateFields fs = _updateFieldsBy @e fs (primaryKey @e)

{-| Produce an UPDATE statement for the given entity and fields, by the specified field.
{-| Produce an UPDATE statement for the given entity and fields, by the specified fields.

>>> _updateFieldsBy @Author [ [field| name |] ] [field| name |]
"UPDATE \"authors\" SET (\"name\") = ROW(?) WHERE \"name\" = ?"
>>> _updateFieldsWhere @Author [[field| name |]] [[field| author_id |], [field| name |]]
"UPDATE \"authors\" SET (\"name\") = ROW(?) WHERE \"author_id\" = ? AND \"name\" = ?"

>>> _updateFieldsBy @BlogPost [[field| author_id |], [field| title |]] [field| title |]
"UPDATE \"blogposts\" SET (\"author_id\", \"title\") = ROW(?, ?) WHERE \"title\" = ?"
>>> _updateFieldsWhere @BlogPost [[field| author_id |], [field| title |]] [[field| blogpost_id |], [field| title |]]
"UPDATE \"blogposts\" SET (\"author_id\", \"title\") = ROW(?, ?) WHERE \"blogpost_id\" = ? AND \"title\" = ?"

@since 0.0.1.0
@since TODO
-}
_updateFieldsBy
_updateFieldsWhere
:: forall e
. Entity e
=> Vector Field
-- ^ Field names to update
-> Field
-> Vector Field
-- ^ Field on which to match
-> Query
_updateFieldsBy fs' f =
_updateFieldsWhere fs' f =
textToQuery
( "UPDATE "
<> getTableName @e
Expand All @@ -710,14 +734,34 @@ _updateFieldsBy fs' f =
<> " = "
<> newValues
)
<> _where [f]
<> _where f

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah but we have a problem here, if f is an empty list we will get a runtime error. We should either document this invariant or guard against it in the code.

where
fs = V.filter (/= (primaryKey @e)) fs'
newValues = "ROW" <> inParens (generatePlaceholders fs)
updatedFields =
inParens $
V.foldl1' (\element acc -> element <> ", " <> acc) (quoteName . fieldName <$> fs)

{-| Produce an UPDATE statement for the given entity and fields, by the specified field.

>>> _updateFieldsBy @Author [ [field| name |] ] [field| name |]
"UPDATE \"authors\" SET (\"name\") = ROW(?) WHERE \"name\" = ?"

>>> _updateFieldsBy @BlogPost [[field| author_id |], [field| title |]] [field| title |]
"UPDATE \"blogposts\" SET (\"author_id\", \"title\") = ROW(?, ?) WHERE \"title\" = ?"

@since 0.0.1.0
-}
_updateFieldsBy
:: forall e
. Entity e
=> Vector Field
-- ^ Field names to update
-> Field
-- ^ Field on which to match
-> Query
_updateFieldsBy fs' = _updateFieldsWhere @e fs' . V.singleton

{-| Produce a DELETE statement for the given entity, with a match on the Primary Key

__Examples__
Expand Down
14 changes: 14 additions & 0 deletions test/EntitySpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
, selectWhereNull
, update
, updateFieldsBy
, updateFieldsWhere
, _joinSelectWithFields
, _where
)
Expand Down Expand Up @@ -71,7 +72,7 @@
selectBlogPostByTitle :: TestM ()
selectBlogPostByTitle = do
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate
blogPost <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 75 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 75 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 75 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 75 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 75 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
result <- liftDB $ selectOneByField @BlogPost [field| title |] (Only (blogPost ^. #title))
U.assertEqual result (Just blogPost)

Expand All @@ -79,10 +80,10 @@
selectByNullAndNonNull = do
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate

liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 83 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 83 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 83 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 83 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 83 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 84 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 84 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 84 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 84 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 84 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 85 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 85 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 85 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 85 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 85 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 86 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 86 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 86 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 86 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 86 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

result <- liftDB $ selectWhereNotNull @BlogPost [[field| author_id |], [field| title |]]
U.assertEqual True (not . null $ result)
Expand All @@ -93,9 +94,9 @@
selectManyByAuthorId = do
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate

blogPost4 <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 97 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 97 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 97 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 97 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 97 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
blogPost2 <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 98 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 98 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 98 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 98 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 98 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
blogPost3 <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 99 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 99 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 99 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 99 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.8.4 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.

Check warning on line 99 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.10.2 on ubuntu-latest

Ambiguous record update with parent type constructor ‘RandomBlogPostTemplate’.
result <- liftDB $ selectManyByField @BlogPost [field| author_id |] $ Only (#authorId blogPost4)
U.assertEqual (Set.fromList [blogPost2, blogPost3, blogPost4]) (Set.fromList $ V.toList result)

Expand All @@ -104,7 +105,7 @@
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate

blogPost1 <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateTitle = pure "Echoes from the other world", generateAuthorId = pure (author ^. #authorId)}
blogPost2 <- liftDB $ instantiateRandomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 108 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.2.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 108 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.4.8 on ubuntu-latest

The record update randomBlogPostTemplate

Check warning on line 108 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate
liftDB $ delete @BlogPost (Only (blogPostId blogPost2))
result <- liftDB $ selectManyByField @BlogPost [field| blogpost_id |] $ Only (#blogPostId blogPost2)
U.assertEqual 0 (V.length result)
Expand Down Expand Up @@ -144,7 +145,7 @@
changeAuthorName = do
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate

blogPost <- randomBlogPost randomBlogPostTemplate{generateAuthorId = pure (author ^. #authorId)}

Check warning on line 148 in test/EntitySpec.hs

View workflow job for this annotation

GitHub Actions / 9.6.7 on ubuntu-latest

The record update randomBlogPostTemplate
liftDB $ insertBlogPost blogPost

let newAuthor = author{name = "Hannah Kürsch"}
Expand All @@ -165,6 +166,19 @@
result3 <- liftDB $ updateFieldsBy @Author [[field| name |]] ([field| name |], oldName) (Only newName)
U.assertEqual 1 result3

let staleName = "Jane McElroy" :: Text
liftDB $ instantiateRandomAuthor randomAuthorTemplate{generateName = pure staleName}
let freshName = "Sarah McElroy" :: Text
result4 <- liftDB $ updateFieldsWhere @Author [[field| name |]] [[field| name |]] (freshName, staleName)
U.assertEqual 1 result4

let previousName = "Andrew Garfield" :: Text
currentName = "Cat Garfield" :: Text
author3 <- liftDB $ instantiateRandomAuthor randomAuthorTemplate{generateName = pure previousName}
let newAuthor3Id = UUID.toText $ getAuthorId $ #authorId author3 :: Text
result5 <- liftDB $ updateFieldsWhere @Author [[field| name |]] [[field| author_id |], [field| name |]] (currentName, newAuthor3Id, previousName)
U.assertEqual 1 result5

selectWhereIn :: TestM ()
selectWhereIn = do
author <- liftDB $ instantiateRandomAuthor randomAuthorTemplate
Expand Down
Loading