Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PersistLiteral pattern synonym #1205

Merged
merged 4 commits into from
Mar 26, 2021
Merged

PersistLiteral pattern synonym #1205

merged 4 commits into from
Mar 26, 2021

Conversation

parsonsmatt
Copy link
Collaborator

@parsonsmatt parsonsmatt commented Mar 15, 2021

Before submitting your PR, check that you've:

After submitting your PR:

  • Update the Changelog.md file with a link to your PR
  • Bumped the version number if there isn't an (unreleased) on the Changelog
  • Check that CI passes (or if it fails, for reasons unrelated to your change, like CI timeouts)

This PR provides a fix for #1161. Briefly, we replace the various PeristLiteral constructors with pattern synonyms that back the PersistLiteral_ constructor, which accepts a LiteralKind value. The pattern synonyms can be used to construct a value as expected, but in pattern matching, it actually ignores the LiteralKind value.

This approach has few downsides, documented in the #1161 discussion. To summarize, a pattern match on PersistDbSpecific bs expands into a match on PersistLiteral_ _kind bs, which means that it will always succeed. Patterns that attempted to disambiguate and provide difference behavior based on PersistDbSpecific vs PersistLiteral will always hit the first case that matches instead of differentiating. Specifically, if it hits a PersistDbSpecific bs and delegates to PersistLiteral bs, then it will loop.

Copy link
Collaborator Author

@parsonsmatt parsonsmatt left a comment

Choose a reason for hiding this comment

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

This should occur in two steps.

First, I want to get a patch release out ASAP for 2.11 that modifies the deprecation message. Users should know to continue pattern matching on PersistDbSpecific even if they've added a case for PersistLiteral. I'll write an issue with the relevant information, since it's a pretty specific code path it has to go down. Users that upgrade, see the patch, and include both patterns will work fine.

Second, we can roll this change out. Since it's a breaking change, it'll go out with 2.12. But users that upgrade directly to 2.12 should not observe any problematic behavior.

Why even do this change?

Uhhhh that's a good question. We have deprecated the PersistDbSpecific constructor, so folks want to get rid of it to clean up warnings. But we're now requiring that you keep it around for backwards compatibility on the data loading side! THat's unfortunate.

So this PR can allow you to have a backwards-compatible deserialization logic while avoiding any deprecated terms.

render (P (PersistLiteralEscaped e)) = MySQL.Escape e
render (P (PersistLiteral_ DbSpecific s)) = MySQL.Plain $ BBS.fromByteString s
render (P (PersistLiteral_ Unescaped l)) = MySQL.Plain $ BBS.fromByteString l
render (P (PersistLiteral_ Escaped e)) = MySQL.Escape e
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This needed to be expanded out, because PersistDbSpecific will always match on any PersistLiteral_ constructor. Which means the other two patterns would never hit. Expanding it out works like you should expect.

@@ -626,8 +626,9 @@ fromPersistValueError haskellType databaseType received = T.concat

instance PersistField PgInterval where
toPersistValue = PersistLiteralEscaped . pgIntervalToBs
fromPersistValue (PersistDbSpecific bs) = fromPersistValue (PersistLiteralEscaped bs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This line specifically will loop forever at runtime. A pretty nasty bug, fortunately easy to solve.

PersistLiteralEscaped bs = PersistLiteral_ Escaped bs

pattern PersistLiteral bs <- PersistLiteral_ _ bs where
PersistLiteral bs = PersistLiteral_ Unescaped bs
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the meat of the change here. Note that the pattern match ignores the LiteralType argument.

Therefore, a FromJSON instance can return a PersistLiteral_ DbSpecific bs, and pattern matching code using PersistLiteral bs will work (since it expands to PersistLiteral_ _ bs).

@parsonsmatt
Copy link
Collaborator Author

I've tried this out on the Mercury codebase, and we were able to load all of our models successfully with this branch. I'm going to move forward with the plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant