Use backend-specific upsertBy#856
Conversation
|
Tests currently fail with format errors: It seems to require a condition for every unique key in the |
|
@limick can you merge the latest master to rerun CI against the new test suite? I think we can get this in for 2.9.2 once tests are passing. |
|
I think you may have discovered a bug! The code in the Postgresql for upsertSql' :: EntityDef -> Text -> Text
upsertSql' ent updateVal = T.concat
[ "INSERT INTO "
, escape (entityDB ent)
, "("
, T.intercalate "," $ map (escape . fieldDB) $ entityFields ent
, ") VALUES ("
, T.intercalate "," $ map (const "?") (entityFields ent)
, ") ON CONFLICT ("
, T.intercalate "," $ concat $ map (\x -> map escape (map snd $ uniqueFields x)) (entityUniques ent)
, ") DO UPDATE SET "
, updateVal
, " WHERE "
, wher
, " RETURNING ??"
]
where
wher = T.intercalate " AND " $ map singleCondition $ entityUniques ent
singleCondition :: UniqueDef -> Text
singleCondition udef = T.intercalate " AND " (map singleClause $ map snd (uniqueFields udef))
singleClause :: DBName -> Text
singleClause field = escape (entityDB ent) <> "." <> (escape field) <> " =?"The So, I think we need to alter the # persistent/Database/Persist/Sql?Types/Internal.hs
# line 89
- , connUpsertSql :: Maybe (EntityDef -> Text -> Text)
+ , connUpsertSql :: Maybe (EntityDef -> NonEmpty UniqueDef -> Text -> Text)Then, in the |
|
Yes, the fact that we had a clause per unique condition was part of what I found confusing. |
|
Neither MySQL nor SQLite have |
|
Does this PR still have a purpose after the merged PR #895 from @parsonsmatt ? |
|
Yes, it still has a purpose because the aim is to use upsert where available, which is worth fixing and not fixed by anything else as far as I know. |
I implemented the changes as suggested by @parsonsmatt and the tests pass. |
parsonsmatt
left a comment
There was a problem hiding this comment.
Ah, crap, sorry, I started a review a long time ago but never actually finalized it.
| -- backends that support this functioanlity. If 'Nothing', rows will be | ||
| -- inserted one-at-a-time using 'connInsertSql'. | ||
| , connUpsertSql :: Maybe (EntityDef -> NonEmpty UniqueDef -> Text -> Text) | ||
| , connUpsertSql :: Maybe (EntityDef -> NonEmpty (HaskellName,DBName) -> Text -> Text) |
There was a problem hiding this comment.
This is a major-bump change since it's changing the type definition of a public datatype. Is this necessary?
There was a problem hiding this comment.
I think it's necessary, because I don't see a way to go from Unique record (which is the type of uniqueKey in Persist/Sql/Orphan/PersistUnique.hs) to its corresponding UniqueDef.
I'd be glad to be proven wrong.
There was a problem hiding this comment.
Hmmm. That's irritating.
Looks like we have this possible pathway:
- With a
PersistEntity ein scope, we can calllet uniqDefs = entityUniques (entityDef (Proxy @e)) :: [UniqueDef]. - Then we have
persistUniqueToFieldNames :: Unique record -> [(HaskellName, DBName)]. - We can do a lookup on the
[UniqueDef]to find the correspondinguniqueFields- egfind (\u -> uniqueFields u == persistUniqueToFieldNames uniq) uniqDefs
Not the most elegant solution, for sure, but it will work I think.
|
Going to get this merged in for a 2.11 release soon. |
This fixes #855, where
upsertByonly ever used the default implementation.Before submitting your PR, check that you've:
@sincedeclarations to the HaddockAfter submitting your PR: