yesodweb / yesodweb/persistent

persist-template: Thoughts on adding a function to derive Postgres enums?

Open
#792 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Haskell
Stars
486
Forks
306
PR merge metrics
No merged PRs in 30d

Description

Postgres enum support is a widely requested feature for Persistent. However, that's a moderately difficult project and hasn't seen any progress in several years.

One way users could start benefitting from Postgres enums now is by starting with support for deriving PersistField and PersistFieldSql. This wouldn't help users who are 100% relying on Persistent to generate their types, but it would help:

  1. People who manually migrate. I believe many production users of Persistent write their own migrations anyway, because they don't want to run automatic migrations on a production system (e.g. @bitemyapp does this)
  2. People who run some migrations manually before Persistent's automatic ones. Some users do this already to e.g. enable Postgres extensions; they could create their enums here as well.
  3. People connecting to an existing database

How would people feel about adding this function (possibly marked as experimental?) to Database.Persist.TH:

derivePostgresEnum :: String -> String -> Q [Dec]
derivePostgresEnum s postgresType = do
    ss <- [|SqlOther (pack postgresType)|]
    tpv <- [|PersistText . pack . show|]
    fpv <- [|\dt v ->
                case fromPersistValue v of
                    Left e -> Left e
                    Right s' ->
                        case reads $ unpack s' of
                            (x, _):_ -> Right x
                            [] -> Left $ pack "Invalid " ++ pack dt ++ pack ": " ++ s'|]
    return
        [ persistFieldInstanceD False (ConT $ mkName s)
            [ FunD 'toPersistValue
                [ normalClause [] tpv
                ]
            , FunD 'fromPersistValue
                [ normalClause [] (fpv `AppE` LitE (StringL s))
                ]
            ]
        , persistFieldSqlInstanceD False (ConT $ mkName s)
            [ sqlTypeFunD ss
            ]
        ]

Example usage:

data Pixel = Green | Red | Blue
    deriving (Show, Read, Eq)

derivePostgresEnum "Pixel" "pixel"

I do have a selfish motivation for this as well: We would like to use this function at work. I wrote a version that doesn't use any private internals of persistent-th, but it's extremely ugly without persistent-th's many private helper functions.

cc @eugenk @jairoGilC @danclien

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Database.Persist.TH and compare the proposed derivePostgresEnum with the linked gist, focusing on the persistent-th helper functions mentioned in the issue. Before implementation, resolve the API and experimental-status questions; the issue does not name tests or define a settled completion criterion.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell, postgresql
Domain
databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.