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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions