yesodweb / persistent

Persistence interface for Haskell allowing multiple storage methods.
MIT License
467 stars 297 forks source link

Marshal error on list of pairs #1548

Open marcosh opened 2 months ago

marcosh commented 2 months ago

Using

stack 2.15.3 x86_64 hpack-0.36.0
GHC 9.4.8
psql (PostgreSQL) 15.6

With a model like

data PersonT = Adults | Students

Booking

PersonType
  type PersonT

BookingPerson
  booking BookingId
  personType PersonTypeId

if I perform an equeleto query like

qFoo :: DB [(Entity Booking, Value [(PersonT, Int)])]
qFoo = do
  select $ do
    booking :& bookingPerson :& personType <-
      from $
        table @Booking
          `leftJoin` table @BookingPerson
            `on` (\(booking :& bookingPerson) -> just booking.id ==. bookingPerson.booking)
          `leftJoin` table @PersonType
            `on` (\(_ :& bookingPerson :& personType) -> bookingPerson.personType ==. personType.id)
    groupBy booking.id
    pure
      ( booking
      , arrayRemoveNull $ arrayAgg (pair (personType ?. #type) bookingPerson.amount)
      )

where pair is an helper function to pass tuples to postgresql defined as

pair :: SqlExpr (Value a) -> SqlExpr (Value b) -> SqlExpr (Value (a, b))
pair (ERaw _ f1) (ERaw _ f2) = ERaw noMeta f
 where
  f p info =
    let (b1, vals1) = f1 Parens info
        (b2, vals2) = f2 Parens info
     in ( parensM p ("(" <> b1 <> ", " <> b2 <> ")")
        , vals1 <> vals2
        )

then the following query is correctly run

SELECT "booking"."id", array_remove(array_agg(("person_type"."type", "booking_person"."amount")), NULL)
FROM "booking"
LEFT OUTER JOIN "booking_person" ON "booking"."id" = "booking_person"."booking"
LEFT OUTER JOIN "person_type" ON  "booking_person"."person_type" = "person_type"."id"
GROUP BY "booking"."id"

but I get the following error

PersistMarshalError "Failed to parse Haskell type `List`; expected list, string, bytestring or null from database, but received: PersistLiteral_ Escaped \"{\\\"(Adults,2)\\\",\\\"(Children,7)\\\"}\". Potential solution: Check that your database schema matches your Persistent model definitions."