Skip to content

Insert with defaults: nextval doesn't handle newtype-wrapped Id #328

@ulidtko

Description

@ulidtko

Hi! 👋 Gr8 lib 😂

The facilities around column defaulting though... come across a bit underwhelming.

For sake of example, suppose a table like so:

newtype ClientId = ClientId Int64
  deriving newtype Show
  deriving newtype (DBType, DBEq)

-- | HKD model of an entry in the @client@ table.
data EntryClient f = EntryClient
  { clientId  :: Column f ClientId
  , name      :: Column f Text
  , domain    :: Column f Text
  , last_seen :: Column f UTCTime
  }
  deriving stock Generic
  deriving anyclass Rel8able

deriving instance Show (EntryClient Result)

clientTable :: TableSchema (EntryClient Name)
clientTable = TableSchema
  { name = "client"
  , columns = namesFromLabels { clientId = "id" }
  }

Pretty bland & by-the-book, right?..

But then, it seems Insert can't be written without unsafeDefault:

upsertClient :: Text -> Text -> Insert (Query (Expr ClientId))
upsertClient (litExpr -> name') (litExpr -> domain')
  = Insert
    { into = clientTable
    , rows = values
      [ EntryClient
        { name = name'
        , domain = domain'
        , clientId = unsafeDefault
        --, clientId = nextval "client_id_seq" <&> ClientId -- nope! Expr isn't Functor
        , last_seen = Rel8.Expr.Time.now
        }
      ]
    -- ...

Ideally, as a user I'd love to skip writing out columns with DEFAULT altogether, in the spirit of raw SQL:

  INSERT INTO client (name, domain) VALUES (%s, %s)
  ON CONFLICT (name, domain) DO UPDATE SET last_seen=NOW()
  RETURNING id

— but it seems this may require something like #216.

Or am I missing a more advanced use of values ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions