|
1 | 1 | module WebRow.Selda where
|
2 | 2 |
|
3 | 3 | import Prelude
|
4 |
| -import Control.Monad.Reader (ask) |
5 |
| -import Data.Either (either) |
6 |
| -import Data.Maybe (Maybe, maybe) |
7 |
| -import Database.PostgreSQL (class FromSQLRow, PGError, Row0(..)) |
8 |
| -import Database.PostgreSQL as PG |
9 |
| -import Effect.Aff (Aff, throwError) |
10 |
| -import Effect.Aff.Class (liftAff) |
11 |
| -import Selda.PG.Class (class MonadSeldaPG) |
12 |
| - |
13 |
| -pgExecute ∷ PG.Connection → String → Aff (Maybe PGError) |
14 |
| -pgExecute conn q = PG.execute conn (PG.Query q) Row0 |
15 |
| - |
16 |
| -exec ∷ ∀ m. MonadSeldaPG m ⇒ String → m Unit |
17 |
| -exec q = do |
18 |
| - conn ← ask |
19 |
| - merr ← liftAff $ pgExecute conn q |
20 |
| - maybe (pure unit) throwError merr |
21 |
| - |
22 |
| -queryPG ∷ ∀ m a. MonadSeldaPG m ⇒ FromSQLRow a ⇒ String → m (Array a) |
23 |
| -queryPG q = do |
24 |
| - conn ← ask |
25 |
| - (liftAff $ PG.query conn (PG.Query q) Row0) |
26 |
| - >>= either throwError pure |
| 4 | +import Control.Monad.Except (ExceptT, runExceptT) |
| 5 | +import Control.Monad.Reader (ReaderT, runReaderT) |
| 6 | +import Data.Either (Either(..)) |
| 7 | +import Data.Maybe (Maybe(..)) |
| 8 | +import Data.Symbol (SProxy(..)) |
| 9 | +import Data.Variant.Internal (FProxy) |
| 10 | +import Database.PostgreSQL (Connection, PGError, Query(..), execute) as PG |
| 11 | +import Database.PostgreSQL (PGError, Row0(..)) |
| 12 | +import Database.PostgreSQL (class FromSQLRow, class ToSQLRow, Query, query) as PostgreSQL |
| 13 | +import Effect.Aff (Aff) |
| 14 | +import Run (Run) |
| 15 | +import Run as Run |
| 16 | +import Run.Except (EXCEPT, throwAt) |
| 17 | +import Run.Reader (READER, askAt) |
| 18 | +import Selda (Col, FullQuery, Table) |
| 19 | +import Selda.PG.Class (class InsertRecordIntoTableReturning, BackendPGClass) |
| 20 | +import Selda.PG.Class (deleteFrom, insert, insert1, insert1_, query, query1, update) as PG.Class |
| 21 | +import Selda.Query.Class (class GenericDelete, class GenericInsert, class GenericQuery, class GenericUpdate) |
| 22 | +import Type.Row (type (+)) |
| 23 | +import WebRow.Contrib.Run (AffRow) |
| 24 | + |
| 25 | +type SeldaPG |
| 26 | + = ExceptT PG.PGError (ReaderT PG.Connection Aff) |
| 27 | + |
| 28 | +-- | I'm not sure if this represtation is consistent. |
| 29 | +-- | I want to allow some inspection but also compile |
| 30 | +-- | those actions without coercing. |
| 31 | +-- | |
| 32 | +-- | * Probably we could split query and keep `FullQuery`. |
| 33 | +-- | * We can add `∀ i o. Query i o` to `PgQueryF`. |
| 34 | +data SeldaF a |
| 35 | + = SeldaF (SeldaPG a) |
| 36 | + | PgExecuteF String (Unit → a) |
| 37 | + | PgQueryF (PG.Connection → Aff (Either PGError a)) |
| 38 | + |
| 39 | +derive instance functorSeldaF ∷ Functor SeldaF |
| 40 | + |
| 41 | +type SELDA |
| 42 | + = FProxy SeldaF |
| 43 | + |
| 44 | +type Selda eff |
| 45 | + = ( selda ∷ SELDA | eff ) |
| 46 | + |
| 47 | +_selda = SProxy ∷ SProxy "selda" |
| 48 | + |
| 49 | +type PgError r |
| 50 | + = ( pgError ∷ EXCEPT PGError | r ) |
| 51 | + |
| 52 | +type PgConnection r |
| 53 | + = ( pgConnection ∷ READER { conn ∷ PG.Connection, inTransaction ∷ Boolean } | r ) |
| 54 | + |
| 55 | +_pgConnection = SProxy ∷ SProxy "pgConnection" |
| 56 | + |
| 57 | +_pgError = SProxy ∷ SProxy "pgError" |
| 58 | + |
| 59 | +query ∷ |
| 60 | + ∀ eff o i. |
| 61 | + GenericQuery BackendPGClass SeldaPG i o ⇒ |
| 62 | + FullQuery BackendPGClass { | i } → |
| 63 | + Run (Selda + eff) (Array { | o }) |
| 64 | +query q = do |
| 65 | + Run.lift _selda (SeldaF (PG.Class.query q)) |
| 66 | + |
| 67 | +query1 ∷ |
| 68 | + ∀ eff o i. |
| 69 | + GenericQuery BackendPGClass SeldaPG i o ⇒ |
| 70 | + FullQuery BackendPGClass { | i } → Run (Selda + eff) { | o } |
| 71 | +query1 q = Run.lift _selda (SeldaF (PG.Class.query1 q)) |
| 72 | + |
| 73 | +insert ∷ |
| 74 | + ∀ eff r t ret. |
| 75 | + InsertRecordIntoTableReturning r t ret ⇒ |
| 76 | + Table t → Array { | r } → Run (Selda + eff) (Array { | ret }) |
| 77 | +insert table xs = do |
| 78 | + Run.lift _selda (SeldaF (PG.Class.insert table xs)) |
| 79 | + |
| 80 | +insert1_ ∷ |
| 81 | + ∀ eff t r. |
| 82 | + GenericInsert BackendPGClass SeldaPG t r ⇒ |
| 83 | + Table t → { | r } → Run (Selda + eff) Unit |
| 84 | +insert1_ table r = Run.lift _selda (SeldaF (PG.Class.insert1_ table r)) |
| 85 | + |
| 86 | +insert1 ∷ |
| 87 | + ∀ eff r t ret. |
| 88 | + InsertRecordIntoTableReturning r t ret ⇒ |
| 89 | + Table t → { | r } → Run (Selda + eff) { | ret } |
| 90 | +insert1 table r = Run.lift _selda (SeldaF (PG.Class.insert1 table r)) |
| 91 | + |
| 92 | +deleteFrom ∷ |
| 93 | + ∀ eff t r. |
| 94 | + GenericDelete BackendPGClass SeldaPG t r ⇒ |
| 95 | + Table t → ({ | r } → Col BackendPGClass Boolean) → Run (Selda + eff) Unit |
| 96 | +deleteFrom table r = Run.lift _selda (SeldaF (PG.Class.deleteFrom table r)) |
| 97 | + |
| 98 | +update ∷ |
| 99 | + ∀ eff t r. |
| 100 | + GenericUpdate BackendPGClass SeldaPG t r ⇒ |
| 101 | + Table t → ({ | r } → Col BackendPGClass Boolean) → ({ | r } → { | r }) → Run (Selda + eff) Unit |
| 102 | +update table cond set = Run.lift _selda (SeldaF (PG.Class.update table cond set)) |
| 103 | + |
| 104 | +pgExecute ∷ ∀ eff. String → Run (Selda + eff) Unit |
| 105 | +pgExecute q = Run.lift _selda (PgExecuteF q identity) |
| 106 | + |
| 107 | +pgQuery ∷ |
| 108 | + ∀ eff i o. |
| 109 | + PostgreSQL.ToSQLRow i ⇒ |
| 110 | + PostgreSQL.FromSQLRow o ⇒ |
| 111 | + PostgreSQL.Query i o → |
| 112 | + i → |
| 113 | + Run (Selda + eff) (Array o) |
| 114 | +pgQuery q i = Run.lift _selda (PgQueryF (\conn → PostgreSQL.query conn q i)) |
| 115 | + |
| 116 | +run ∷ |
| 117 | + ∀ eff. |
| 118 | + Run (AffRow + PgConnection + PgError + Selda + eff) |
| 119 | + ~> Run (AffRow + PgConnection + PgError + eff) |
| 120 | +run = Run.run (Run.on _selda handleSelda Run.send) |
| 121 | + where |
| 122 | + handleSelda ∷ ∀ a. SeldaF a → Run (AffRow + PgConnection + PgError + eff) a |
| 123 | + handleSelda action = do |
| 124 | + { conn, inTransaction } ← askAt _pgConnection |
| 125 | + case action of |
| 126 | + SeldaF q → do |
| 127 | + Run.liftAff (runReaderT (runExceptT q) conn) |
| 128 | + >>= case _ of |
| 129 | + Right next → pure next |
| 130 | + Left err → throwAt _pgError err |
| 131 | + PgExecuteF q next → do |
| 132 | + (Run.liftAff $ PG.execute conn (PG.Query q) Row0) |
| 133 | + >>= case _ of |
| 134 | + Just err → throwAt _pgError err |
| 135 | + Nothing → pure (next unit) |
| 136 | + PgQueryF q → do |
| 137 | + (Run.liftAff (q conn)) |
| 138 | + >>= case _ of |
| 139 | + Left err → throwAt _pgError err |
| 140 | + Right next → pure next |
0 commit comments