Open tomjaguarpaw opened 4 years ago
The Haddock documentation for ´toFields´ provides an example with runInsert
. However, runInsert
is deprecated and should be replaced with runInsert_
. It would be great to have an example how to use toFields
when setting up an Insert
record. How exactly should the iRows
field look like?
Yes, see d4c433984f8efb369a0a21220d249689885c5553 (which will be in 0.7).
Thanks for the updated documentation!
Unfortunately, I still don't understand how to use the toFields
mapping when the insert column-record has an optional field - in my case, an autogenerated key - probably because I still do not fully understand the product-profunctor magic.
Simplified setup:
-- Helper
type F field = OE.Field field
-- Newtype wrapper for distinct primary keys
newtype UserIdT a = UserId {getUserId :: a}
deriving (Eq, Show, Display)
$(makeAdaptorAndInstance "pUserId" ''UserIdT)
-- Table definition
type UserIdField = UserIdT (F OE.SqlInt8)
type OptionalUserIdField = UserIdT (Maybe (F OE.SqlInt8))
type UserId = UserIdT Int64
data UserT key username
= User
{ userKey :: !key,
userUsername :: !username }
type UserW
= UserT
OptionalUserIdField -- autogenerated key
(F OE.SqlText) -- username
type UserR
= UserT
UserIdField -- key
(F OE.SqlText) -- username
-- The concrete Haskell type, including the key. However, the key is not optional here.
type User
= UserT UserId -- key
Text -- username
$(makeAdaptorAndInstance "pUser" ''UserT)
userTable :: OE.Table UserW UserR
userTable = OE.tableWithSchema
schemaName
userTableName
(pUser User { userKey = pUserId (UserId (OE.tableField "id"))
, userUsername = OE.tableField "username"
}
)
How does the haskells record need to look like so that toFields
can correctly convert it into a Fields
record? Do I need to wrap the key into a Maybe
and set it to Nothing
? With the above definition, I cannot construct a proper Insert
:
newUser = User
{ UT.userKey = UT.UserId 123
, UT.userUsername = "tomTester" }
-- does not type check, probably because the key is not optional.
insertSpec = OE.Insert { OE.iTable = userTable
, OE.iRows = map OE.toFields [newUser]
, OE.iReturning = OE.rCount
, OE.iOnConflict = Nothing
}
Hi Uli, the quick answer is to use
newUser = User
{ userKey = UserId (Nothing :: Maybe Int64)
, userUsername = "tomTester" }
I will think about how to make this more obvious and get back to you.
Ok. Basically, I need to define different haskells types for reading and writing to the DB - the User
type I defined above for reading, and the type of newUser
from your answer for writing to the DB?
Thanks for the help so far. I have one more issue to make insertions work. In the example above, the insertSpec
simply returned the number of correctly inserted row. In a real application, though. with autogenerated keys, I would like the insert to return the primary key of the newly inserted row. I tried to modify the insertSpec
as follows:
insertSpec = OE.Insert { OE.iTable = userTable
, OE.iRows = map OE.toFields [newUser]
, OE.iReturning = OE.rReturning (\User{userKey = pk} -> pk)
, OE.iOnConflict = Nothing
}
However, I get the following type checker error
• Couldn't match type ‘[haskells0]’ with ‘UserIdT Int64’
Expected type: Returning
(UserT
(UserIdT (Column PGInt8))
(Column PGText)
(Column PGText)
(Column (Nullable PGText))
(Column (Nullable PGText))
(Column PGText))
UserId
Actual type: Returning
(UserT
(UserIdT (Column PGInt8))
(Column PGText)
(Column PGText)
(Column (Nullable PGText))
(Column (Nullable PGText))
(Column PGText))
[haskells0]
Again, I guess I am lost with the product pro functor magic going on here. The type of the private key is indeed UserIdT Int64
(a newtype wrapper). How should I annotate that type?
It will return a list of values, even if you only insert one row. You will have to make sure you treat the the return value as something of type [UserId]
rather than UserId
.
That was it, thanks a lot for pointing out my mistake. Indeed, it was right in front of me: The error message quoted above was asking for a '[haskells0]' value, which clearly is a list of haskells
, yet I didn't realize the list, but instead was confused by generic 'haskells0' type variable.
Yes, the unhelpful error message is because of the type class constraint. I can make a version of runInsert_
that is more inferrable, like runSelectI
.
I added rReturningI
to 0.7.1.0. It will make the error message much better in this case.
https://github.com/tomjaguarpaw/haskell-opaleye/issues/433#issuecomment-542335898