persistent-typed-db
persistent-typed-db copied to clipboard
Provide an example implementation
Hi! I'm trying to use this library to have my Yesod app use both a postgres and a mysql databases.
I'll call the dbs master and liv.
In Application.hs's makeFoundation function I have:
let mkFoundation appConnPool livConnPool = App {..}
tempFoundation = mkFoundation (error "connPool forced in tempFoundation") (error "X")
logFunc = messageLoggerSource tempFoundation appLogger
pool <- flip runLoggingT logFunc $ createPostgresqlPool
(pgConnStr $ appDatabaseConf appSettings)
(pgPoolSize $ appDatabaseConf appSettings)
livpool <- flip runLoggingT logFunc $ createMySQLPool
(myConnInfo $ livDatabaseConf appSettings)
(myPoolSize $ livDatabaseConf appSettings)
runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc
return $ mkFoundation (specializePool pool) (specializePool livpool)
In Model.hs:
data MasterDB
type MasterQueryT = SqlPersistTFor MasterDB
type MasterQueryM = SqlPersistMFor MasterDB
data LivDB
type LivQueryT = SqlPersistTFor LivDB
type LivQueryM = SqlPersistMFor LivDB
share [mkPersist (mkSqlSettingsFor ''MasterDB), mkMigrate "migrateAll"]
$(persistFileWith lowerCaseSettings "config/models")
share [mkPersist (mkSqlSettingsFor ''LivDB)]
$(persistFileWith lowerCaseSettings "config/livmodels")
Then in Foundation.hs:
data App = App
{ appSettings :: AppSettings
, appStatic :: Static -- ^ Settings for static file serving.
, appConnPool :: ConnectionPoolFor MasterDB -- ^ Database connection pool.
, livConnPool :: ConnectionPoolFor LivDB -- ^ Liv's database connection pool.
, appHttpManager :: Manager
, appLogger :: Logger
}
Now the issue is that the default runDB doesn't typecheck anymore and I wasn't able to fix it. I tried various combinations of generalizeQuery, generalizePool and so on, but I always end up with a Couldn't match type 'SqlBackend' with SqlFor MasterDB' error.
The idea would be to have runDB only run on the MasterDB and then a runLiv function that runs on the LivDB only.
This is what produced the least amount of errors so far:
fromMasterQuery :: ReaderT (SqlFor MasterDB) m a -> ReaderT SqlBackend m a
fromMasterQuery = generalizeQuery
instance YesodPersist App where
type YesodPersistBackend App = SqlFor MasterDB
runDB action = do
conn <- getYesod
runSQlPool (fromMasterQuery action) (generalizePool $ appConnPool conn)
Thanks a lot for any help! :)
What is the type of runDB? I suspect that the type of runDB is somehow using the YesodPersistBackend App type family to choose what the parameter has to be.
Can you post the exact errors you receive?
I have not access to the code right now, but the problem was with runDB and defaultGetDBRunner. They both expect a SqlBackend and not a SqlFor MasterDB.
class Monad (YesodDB site) => YesodPersist site where
type YesodPersistBackend site
runDB :: YesodDB site a -> HandlerT site IO a
defaultGetDBRunner :: (SQL.IsSqlBackend backend, YesodPersistBackend site ~ backend)
=> (site -> Pool backend)
-> HandlerT site IO (DBRunner site, HandlerT site IO ())
Here's my code for this:
-- How to run database actions.
instance YesodPersist App where
type YesodPersistBackend App = SqlFor FbgMasterDb
runDB action = do
master <- getYesod
runSqlPool (generalizeQuery action) (generalizePool $ appConnPool master)
Which... looks exactly like yours. Interesting.
I think the issue is with:
instance YesodPersistRunner App where
getDBRunner = defaultGetDBRunner appConnPool
which gives me this error:
• Couldn't match type ‘SqlFor MasterDB’ with ‘SqlBackend’
arising from a use of ‘defaultGetDBRunner’
• In the expression: defaultGetDBRunner appConnPool
In an equation for ‘getDBRunner’:
getDBRunner = defaultGetDBRunner appConnPool
In the instance declaration for ‘YesodPersistRunner App’
Trying to generalize the appConnPool didn't help. I ended up commenting those two lines and now it seems like I can make queries to the MasterDB correctly.
What I'm missing is a way to query the other db, as doing this in a standard yesod handler doesn't type check:
runLiv :: SqlPersistTFor LivDB (HandlerT App IO) a -> HandlerT App IO a
runLiv action = do
conn <- getYesod
runSqlPool (generalizeQuery action) (generalizePool $ livConnPool conn)
If I try to use this in an Esqueleto query, I get:
• Couldn't match type ‘Database.Persist.Typed.SqlFor LivDB’
with ‘SqlBackend’
arising from a use of ‘E.select’
• In the second argument of ‘($)’, namely
‘E.select
$ E.from
$ \ o
-> do { E.where_ (o E.^. Wp_postsId E.==. E.val 1284);
return o }’
In a stmt of a 'do' block:
posts <- runLiv
$ E.select
$ E.from
$ \ o
-> do { E.where_ (o E.^. Wp_postsId E.==. E.val 1284);
return o }
In the expression:
do { posts <- runLiv $ E.select $ E.from $ \ o -> do { ... };
let oname :: [Text]
oname = map (pack <$> wp_postsPost_status . E.entityVal) wps;
oname }
As a last edit, it works fine for standard persistent queries, I guess I need to figure out how to make it work with esqueleto :)
Sorry for all the noise
Ahh, I am going to be putting together a patch today for Esqueleto to work with the BackendCompatible class :) You can follow here for details.