Skip to content

Commit d117f3d

Browse files
Merge pull request #38 from brandonchinn178/unsafe-lift-sql
Add unsafeLiftSql
2 parents aaabfa5 + f010bee commit d117f3d

13 files changed

+106
-3
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
# 0.3.0.0
4+
5+
* Add `unsafeLiftSql` ([#38](https://github.com/brandonchinn178/persistent-mtl/pull/38))
6+
37
# 0.2.2.0
48

59
* Fix for persistent 2.13

package.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: persistent-mtl
2-
version: 0.2.2.0
2+
version: 0.3.0.0
33
maintainer: Brandon Chinn <[email protected]>
44
synopsis: Monad transformer for the persistent API
55
description: |
@@ -41,6 +41,7 @@ tests:
4141
- bytestring
4242
- conduit
4343
- containers
44+
- esqueleto
4445
- monad-logger
4546
- persistent
4647
- persistent-mtl

persistent-mtl.cabal

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cabal-version: 1.12
55
-- see: https://github.com/sol/hpack
66

77
name: persistent-mtl
8-
version: 0.2.2.0
8+
version: 0.3.0.0
99
synopsis: Monad transformer for the persistent API
1010
description: A monad transformer and mtl-style type class for using the
1111
persistent API directly in your monad transformer stack.
@@ -69,6 +69,7 @@ test-suite persistent-mtl-test
6969
MockSqlQueryT
7070
SqlQueryRepTest
7171
TestUtils.DB
72+
TestUtils.Esqueleto
7273
TestUtils.Match
7374
Paths_persistent_mtl
7475
hs-source-dirs:
@@ -79,6 +80,7 @@ test-suite persistent-mtl-test
7980
, bytestring
8081
, conduit
8182
, containers
83+
, esqueleto
8284
, monad-logger
8385
, persistent
8486
, persistent-mtl

scripts/generate/templates/Shim.mustache

+17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This file is autogenerated, to keep it in sync with
1313
{-# LANGUAGE FlexibleContexts #-}
1414
{-# LANGUAGE GADTs #-}
1515
{-# LANGUAGE PatternSynonyms #-}
16+
{-# LANGUAGE RankNTypes #-}
1617

1718
module Database.Persist.Monad.Shim where
1819

@@ -49,6 +50,22 @@ import Database.Persist.Monad.SqlQueryRep (SqlQueryRep(..))
4950
{{/withCondition}}
5051

5152
{{/functions}}
53+
-- | Lift an arbitrary 'SqlPersistT' action into 'MonadSqlQuery'.
54+
--
55+
-- This is unsafe because the action may be rerun. This function should
56+
-- primarily be used to interop with other libraries built on top of
57+
-- persistent.
58+
--
59+
-- Example usage:
60+
--
61+
-- @
62+
-- -- | Run an esqueleto select.
63+
-- select :: (MonadSqlQuery m, E.SqlSelect a r) => E.SqlQuery a -> m [r]
64+
-- select q = unsafeLiftSql "esqueleto-select" (E.select q)
65+
-- @
66+
unsafeLiftSql :: MonadSqlQuery m => Text -> (forall m2. MonadIO m2 => SqlPersistT m2 a) -> m a
67+
unsafeLiftSql label action = runQueryRep $ UnsafeLiftSql label action
68+
5269
{- Helpers -}
5370

5471
-- | A helper for functions that return a conduit.

scripts/generate/templates/SqlQueryRep.mustache

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This file is autogenerated, to keep it in sync with
1515
{-# LANGUAGE GADTs #-}
1616
{-# LANGUAGE LambdaCase #-}
1717
{-# LANGUAGE PatternSynonyms #-}
18+
{-# LANGUAGE RankNTypes #-}
1819
{-# LANGUAGE ScopedTypeVariables #-}
1920
{-# LANGUAGE TypeApplications #-}
2021

@@ -31,6 +32,7 @@ import Data.Int (Int64)
3132
import Data.Map (Map)
3233
import Data.Proxy (Proxy(..))
3334
import Data.Text (Text)
35+
import qualified Data.Text as Text
3436
import Data.Typeable (Typeable, eqT, typeRep, (:~:)(..))
3537
import Data.Void (Void)
3638
import Database.Persist.Sql as Persist hiding (pattern Update)
@@ -57,13 +59,18 @@ data SqlQueryRep record a where
5759
{{/withCondition}}
5860

5961
{{/sqlQueryRepConstructors}}
62+
-- | Constructor for lifting an arbitrary SqlPersistT action into SqlQueryRep.
63+
UnsafeLiftSql
64+
:: Text -> (forall m. MonadIO m => Persist.SqlPersistT m a) -> SqlQueryRep Void a
65+
6066
instance Typeable record => Show (SqlQueryRep record a) where
6167
show = \case
6268
{{#sqlQueryRepConstructors}}
6369
{{#withCondition}}
6470
{{{nameCapital}}}{} -> "{{{nameCapital}}}{..}" ++ record
6571
{{/withCondition}}
6672
{{/sqlQueryRepConstructors}}
73+
UnsafeLiftSql label _ -> "UnsafeLiftSql{" ++ Text.unpack label ++ "}"
6774
where
6875
record = case recordTypeRep of
6976
Just recordType -> "<" ++ show recordType ++ ">"
@@ -81,3 +88,4 @@ runSqlQueryRep = \case
8188
{{{nameCapital}}} {{#args}}a{{index}} {{/args}}-> Persist.{{{name}}}{{#args}} a{{index}}{{/args}}
8289
{{/withCondition}}
8390
{{/sqlQueryRepConstructors}}
91+
UnsafeLiftSql _ action -> action

src/Database/Persist/Monad/Shim.hs

+17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This file is autogenerated, to keep it in sync with
1313
{-# LANGUAGE FlexibleContexts #-}
1414
{-# LANGUAGE GADTs #-}
1515
{-# LANGUAGE PatternSynonyms #-}
16+
{-# LANGUAGE RankNTypes #-}
1617

1718
module Database.Persist.Monad.Shim where
1819

@@ -516,6 +517,22 @@ transactionUndoWithIsolation
516517
transactionUndoWithIsolation a1 = runQueryRep $ TransactionUndoWithIsolation a1
517518
#endif
518519

520+
-- | Lift an arbitrary 'SqlPersistT' action into 'MonadSqlQuery'.
521+
--
522+
-- This is unsafe because the action may be rerun. This function should
523+
-- primarily be used to interop with other libraries built on top of
524+
-- persistent.
525+
--
526+
-- Example usage:
527+
--
528+
-- @
529+
-- -- | Run an esqueleto select.
530+
-- select :: (MonadSqlQuery m, E.SqlSelect a r) => E.SqlQuery a -> m [r]
531+
-- select q = unsafeLiftSql "esqueleto-select" (E.select q)
532+
-- @
533+
unsafeLiftSql :: MonadSqlQuery m => Text -> (forall m2. MonadIO m2 => SqlPersistT m2 a) -> m a
534+
unsafeLiftSql label action = runQueryRep $ UnsafeLiftSql label action
535+
519536
{- Helpers -}
520537

521538
-- | A helper for functions that return a conduit.

src/Database/Persist/Monad/SqlQueryRep.hs

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This file is autogenerated, to keep it in sync with
1515
{-# LANGUAGE GADTs #-}
1616
{-# LANGUAGE LambdaCase #-}
1717
{-# LANGUAGE PatternSynonyms #-}
18+
{-# LANGUAGE RankNTypes #-}
1819
{-# LANGUAGE ScopedTypeVariables #-}
1920
{-# LANGUAGE TypeApplications #-}
2021

@@ -31,6 +32,7 @@ import Data.Int (Int64)
3132
import Data.Map (Map)
3233
import Data.Proxy (Proxy(..))
3334
import Data.Text (Text)
35+
import qualified Data.Text as Text
3436
import Data.Typeable (Typeable, eqT, typeRep, (:~:)(..))
3537
import Data.Void (Void)
3638
import Database.Persist.Sql as Persist hiding (pattern Update)
@@ -440,6 +442,10 @@ data SqlQueryRep record a where
440442
=> IsolationLevel -> SqlQueryRep Void ()
441443
#endif
442444

445+
-- | Constructor for lifting an arbitrary SqlPersistT action into SqlQueryRep.
446+
UnsafeLiftSql
447+
:: Text -> (forall m. MonadIO m => Persist.SqlPersistT m a) -> SqlQueryRep Void a
448+
443449
instance Typeable record => Show (SqlQueryRep record a) where
444450
show = \case
445451
Get{} -> "Get{..}" ++ record
@@ -546,6 +552,7 @@ instance Typeable record => Show (SqlQueryRep record a) where
546552
#if MIN_VERSION_persistent(2,9,0)
547553
TransactionUndoWithIsolation{} -> "TransactionUndoWithIsolation{..}" ++ record
548554
#endif
555+
UnsafeLiftSql label _ -> "UnsafeLiftSql{" ++ Text.unpack label ++ "}"
549556
where
550557
record = case recordTypeRep of
551558
Just recordType -> "<" ++ show recordType ++ ">"
@@ -662,3 +669,4 @@ runSqlQueryRep = \case
662669
#if MIN_VERSION_persistent(2,9,0)
663670
TransactionUndoWithIsolation a1 -> Persist.transactionUndoWithIsolation a1
664671
#endif
672+
UnsafeLiftSql _ action -> action

stack-ghc-8.10.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
resolver: nightly-2021-06-01
1+
resolver: lts-18.4
22

33
extra-deps:
44
- persistent-postgresql-2.13.0.2

stack-ghc-8.2.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ resolver: lts-11.22
33
extra-deps:
44
- unliftio-core-0.1.2.0
55
- unliftio-pool-0.2.0.0
6+
- esqueleto-3.0.0
67

78
ghc-options:
89
"$locals": -Werror

stack-ghc-8.4.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ resolver: lts-12.26
22

33
extra-deps:
44
- unliftio-pool-0.2.1.0
5+
- esqueleto-3.0.0
56

67
ghc-options:
78
"$locals": -Werror

stack-ghc-8.6.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
resolver: lts-14.27
22

3+
extra-deps:
4+
- esqueleto-3.0.0
5+
36
ghc-options:
47
"$locals": -Werror

test/Integration.hs

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import qualified Data.Map.Strict as Map
1515
import Data.Text (Text)
1616
import qualified Data.Text as Text
1717
import Data.Typeable (Typeable)
18+
#if MIN_VERSION_esqueleto(3,5,0)
19+
import qualified Database.Esqueleto.Experimental as E
20+
#else
21+
import qualified Database.Esqueleto as E
22+
#endif
1823
import Database.Persist.Sql
1924
( Entity(..)
2025
, Migration
@@ -48,6 +53,7 @@ import Control.Monad.IO.Rerunnable (MonadRerunnableIO, rerunnableIO)
4853
import Database.Persist.Monad
4954
import Example
5055
import TestUtils.DB (BackendType(..), allBackendTypes)
56+
import TestUtils.Esqueleto (esqueletoSelect)
5157
import TestUtils.Match (Match(..), (@?~))
5258

5359
tests :: TestTree
@@ -59,6 +65,7 @@ testsWithBackend backendType = testGroup (show backendType)
5965
[ testWithTransaction backendType
6066
, testComposability backendType
6167
, testPersistentAPI backendType
68+
, testInterop backendType
6269
]
6370

6471
testWithTransaction :: BackendType -> TestTree
@@ -822,6 +829,21 @@ testPersistentAPI backendType = testGroup "Persistent API"
822829
#endif
823830
]
824831

832+
testInterop :: BackendType -> TestTree
833+
testInterop backendType = testGroup "Interop with third-party Persistent libraries"
834+
[ testCase "unsafeLiftSql" $ do
835+
let alice = person "Alice"
836+
result <- runTestApp backendType $ do
837+
insert_ alice
838+
esqueletoSelect $
839+
#if MIN_VERSION_esqueleto(3,5,0)
840+
E.from $ E.table @Person
841+
#else
842+
E.from $ \p -> return p
843+
#endif
844+
result @?= [Entity 1 alice]
845+
]
846+
825847
{- Persistent helpers -}
826848

827849
fromPersistValue' :: PersistField a => PersistValue -> a

test/TestUtils/Esqueleto.hs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
4+
module TestUtils.Esqueleto
5+
( esqueletoSelect
6+
) where
7+
8+
#if MIN_VERSION_esqueleto(3,5,0)
9+
import qualified Database.Esqueleto.Experimental as E
10+
import qualified Database.Esqueleto.Internal.Internal as E
11+
#else
12+
import qualified Database.Esqueleto as E
13+
import qualified Database.Esqueleto.Internal.Sql as E
14+
#endif
15+
16+
import Database.Persist.Monad (MonadSqlQuery, unsafeLiftSql)
17+
18+
esqueletoSelect :: (MonadSqlQuery m, E.SqlSelect a r) => E.SqlQuery a -> m [r]
19+
esqueletoSelect q = unsafeLiftSql "esqueleto-select" (E.select q)

0 commit comments

Comments
 (0)