最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

database - Haskell Persistent: upsert to update list fields - Stack Overflow

programmeradmin3浏览0评论

I want to create a table where one of the columns is a list of Int. For example:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
    PersonTest
      name String
      listi [Int]
      UniquePerson name
      deriving Show Eq Ord
|]

Then I want to use upsert to insert a new person if it is not there yet, otherwise I want to update the list to include the int numbers in the new record. I wrote this small test:

test_experiment :: IO ()
test_experiment = do
let p1 = [PersonTest "Dan" [1,2]
          ,PersonTest "Jo" []
          ,PersonTest "Dan" [4]] -- This triggers an update
    upse = (\pl -> runSqlite "dbe.sqlite" $ do
                   runMigration migrateAll
                   mapM_ (\p -> upsert p [PersonTestListi +=. p.personTestListi]) pl )
upse p1 

When I run this I get the runtime error:

PersistMarshalError "getBy: Couldn't parse field listi from table person_test. Failed to parse Haskell type List; expected list, string, bytestring or null from database, but received: PersistByteString "0". Potential solution: Check that your database schema matches your Persistent model definitions."

What am I doing wrong? I was under the impression that this was supported?

Are there workarounds?

I want to create a table where one of the columns is a list of Int. For example:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
    PersonTest
      name String
      listi [Int]
      UniquePerson name
      deriving Show Eq Ord
|]

Then I want to use upsert to insert a new person if it is not there yet, otherwise I want to update the list to include the int numbers in the new record. I wrote this small test:

test_experiment :: IO ()
test_experiment = do
let p1 = [PersonTest "Dan" [1,2]
          ,PersonTest "Jo" []
          ,PersonTest "Dan" [4]] -- This triggers an update
    upse = (\pl -> runSqlite "dbe.sqlite" $ do
                   runMigration migrateAll
                   mapM_ (\p -> upsert p [PersonTestListi +=. p.personTestListi]) pl )
upse p1 

When I run this I get the runtime error:

PersistMarshalError "getBy: Couldn't parse field listi from table person_test. Failed to parse Haskell type List; expected list, string, bytestring or null from database, but received: PersistByteString "0". Potential solution: Check that your database schema matches your Persistent model definitions."

What am I doing wrong? I was under the impression that this was supported?

Are there workarounds?

Share Improve this question edited Feb 14 at 19:41 user3716072 asked Feb 14 at 16:31 user3716072user3716072 1971 gold badge4 silver badges15 bronze badges 2
  • Dumb question, but the message describes a migration. Are you actually doing a migration? If so, are you sure the problem is with the upsert, and not with the migration? – Daniel Wagner Commented Feb 14 at 19:21
  • 1 Not doing a migration. That is just the output of the test harness when it creates the DataBase from scratch. I think it is an issue with upsert, if you remove the third entry in p1 then the database is created successfully. – user3716072 Commented Feb 14 at 19:41
Add a comment  | 

1 Answer 1

Reset to default 0

Sqlite doesn't support arrays, that's why you see that error message. I confirmed that this works without an array:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE UndecidableInstances       #-}
{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE EmptyDataDecls             #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE StandaloneDeriving         #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
import           Control.Monad.IO.Class  (liftIO)
import           Database.Persist
import           Database.Persist.Sqlite
import           Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
PersonTest
    name String
    listi Int
    UniquePerson name
    deriving Show Eq Ord
|]


main :: IO ()
main = runSqlite ":memory:" $ do
    runMigration migrateAll
    let p1 = [PersonTest "Dan" 1
             ,PersonTest "Jo" 2
             ,PersonTest "Dan" 3
             ]
    mapM_ (\p -> upsert p [PersonTestListi +=. (personTestListi p)]) p1
    persons :: [Entity PersonTest] <- selectList [] []
    liftIO $ print persons

One way to solve this issue would be to use JSON's serialization using Aeson support in persistent.

发布评论

评论列表(0)

  1. 暂无评论