@@ -7,34 +7,36 @@ import Data.Either (Either(..))
7
7
import Data.Maybe (Maybe (..))
8
8
import Data.Symbol (SProxy (..))
9
9
import Data.Variant.Internal (FProxy )
10
- import Database.PostgreSQL (Connection , PGError , Query (..), execute ) as PG
11
10
import Database.PostgreSQL (PGError , Row0 (..))
12
- import Database.PostgreSQL (class FromSQLRow , class ToSQLRow , Query , query ) as PostgreSQL
11
+ import Database.PostgreSQL (class FromSQLRow , class ToSQLRow , Connection , PGError , Pool , Query (..), ConnectResult , connect , execute , query ) as PG
13
12
import Effect.Aff (Aff )
14
13
import Run (Run )
15
14
import Run as Run
16
15
import Run.Except (EXCEPT , throwAt )
17
- import Run.Reader ( READER , askAt )
16
+ import Run.State ( STATE , getAt , modifyAt , putAt )
18
17
import Selda (Col , FullQuery , Table )
19
18
import Selda.PG.Class (class InsertRecordIntoTableReturning , BackendPGClass )
20
19
import Selda.PG.Class (deleteFrom , insert , insert1 , insert1_ , query , query1 , update ) as PG.Class
21
20
import Selda.Query.Class (class GenericDelete , class GenericInsert , class GenericQuery , class GenericUpdate )
22
21
import Type.Row (type (+))
23
- import WebRow.Contrib.Run (AffRow )
22
+ import WebRow.Contrib.Run (AffRow , EffRow )
24
23
25
24
type SeldaPG
26
25
= ExceptT PG.PGError (ReaderT PG.Connection Aff )
27
26
28
27
-- | I'm not sure if this represtation is consistent.
29
- -- | I want to allow some inspection but also compile
30
- -- | those actions without coercing.
28
+ -- | I want to allow some intronspection but also compile
29
+ -- | those actions without coercing or experimenting with
30
+ -- | `Exist` here.
31
31
-- |
32
32
-- | * Probably we could split query and keep `FullQuery`.
33
33
-- | * We can add `∀ i o. Query i o` to `PgQueryF`.
34
34
data SeldaF a
35
35
= SeldaF (SeldaPG a )
36
- | PgExecuteF String ( Unit → a )
36
+ | PgExecuteF String a
37
37
| PgQueryF (PG.Connection → Aff (Either PGError a ))
38
+ | PgOpenTransactionF a
39
+ | PgCloseTransactionF a
38
40
39
41
derive instance functorSeldaF ∷ Functor SeldaF
40
42
@@ -50,9 +52,9 @@ type PgError r
50
52
= ( pgError ∷ EXCEPT PGError | r )
51
53
52
54
type PgConnection r
53
- = ( pgConnection ∷ READER { conn ∷ PG.Connection , inTransaction ∷ Boolean } | r )
55
+ = ( pg ∷ STATE { conn ∷ Maybe PG.ConnectResult , inTransaction ∷ Boolean , pool ∷ PG.Pool } | r )
54
56
55
- _pgConnection = SProxy ∷ SProxy " pgConnection "
57
+ _pg = SProxy ∷ SProxy " pg "
56
58
57
59
_pgError = SProxy ∷ SProxy " pgError"
58
60
@@ -101,40 +103,96 @@ update ∷
101
103
Table t → ({ | r } → Col BackendPGClass Boolean ) → ({ | r } → { | r } ) → Run (Selda + eff ) Unit
102
104
update table cond set = Run .lift _selda (SeldaF (PG.Class .update table cond set))
103
105
106
+ openTransaction ∷ ∀ eff . Run (Selda + eff ) Unit
107
+ openTransaction = Run .lift _selda (PgOpenTransactionF unit)
108
+
109
+ closeTransaction ∷ ∀ eff . Run (Selda + eff ) Unit
110
+ closeTransaction = Run .lift _selda (PgCloseTransactionF unit)
111
+
112
+ withTransaction ∷ ∀ a eff . Run (Selda + eff ) a → Run (Selda + eff ) a
113
+ withTransaction block = do
114
+ openTransaction
115
+ r ← block
116
+ closeTransaction
117
+ pure r
118
+
104
119
pgExecute ∷ ∀ eff . String → Run (Selda + eff ) Unit
105
- pgExecute q = Run .lift _selda (PgExecuteF q identity )
120
+ pgExecute q = Run .lift _selda (PgExecuteF q unit )
106
121
107
122
pgQuery ∷
108
123
∀ eff i o .
109
- PostgreSQL .ToSQLRow i ⇒
110
- PostgreSQL .FromSQLRow o ⇒
111
- PostgreSQL .Query i o →
124
+ PG .ToSQLRow i ⇒
125
+ PG .FromSQLRow o ⇒
126
+ PG .Query i o →
112
127
i →
113
128
Run (Selda + eff ) (Array o )
114
- pgQuery q i = Run .lift _selda (PgQueryF (\conn → PostgreSQL .query conn q i))
129
+ pgQuery q i = Run .lift _selda (PgQueryF (\conn → PG .query conn q i))
115
130
116
131
run ∷
117
132
∀ eff .
118
- Run (AffRow + PgConnection + PgError + Selda + eff )
119
- ~> Run (AffRow + PgConnection + PgError + eff )
120
- run = Run .run (Run .on _selda handleSelda Run .send)
133
+ Run (AffRow + EffRow + PgConnection + PgError + Selda + eff )
134
+ ~> Run (AffRow + EffRow + PgConnection + PgError + eff )
135
+ run action = do
136
+ a ← Run .run (Run .on _selda handleSelda Run .send) action
137
+ getAt _pg >>= _.conn
138
+ >>> case _ of
139
+ Just { connection, done } → do
140
+ inTransaction
141
+ >>= flip when do
142
+ execute " ROLLBACK TRANSACTION"
143
+ modifyAt _pg _ { inTransaction = false }
144
+ Run .liftEffect $ done
145
+ modifyAt _pg _ { conn = Nothing }
146
+ Nothing → pure unit
147
+ pure a
121
148
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))
149
+ inTransaction = getAt _pg <#> _.inTransaction
150
+
151
+ conn = do
152
+ pg ← getAt _pg
153
+ case pg.conn of
154
+ Nothing →
155
+ (Run .liftAff (PG .connect pg.pool))
138
156
>>= case _ of
157
+ Right result → do
158
+ putAt _pg (pg { conn = Just result })
159
+ pure result.connection
139
160
Left err → throwAt _pgError err
140
- Right next → pure next
161
+ Just { connection } → pure connection
162
+
163
+ execute q = do
164
+ c ← conn
165
+ (Run .liftAff $ PG .execute c (PG.Query q) Row0 )
166
+ >>= case _ of
167
+ Just err → throwAt _pgError err
168
+ Nothing → pure unit
169
+
170
+ handleSelda ∷ ∀ a . SeldaF a → Run (AffRow + EffRow + PgConnection + PgError + eff ) a
171
+ handleSelda = case _ of
172
+ SeldaF q → do
173
+ c ← conn
174
+ Run .liftAff (runReaderT (runExceptT q) c)
175
+ >>= case _ of
176
+ Right next → pure next
177
+ Left err → throwAt _pgError err
178
+ PgExecuteF q next → do
179
+ execute q
180
+ pure next
181
+ PgQueryF q → do
182
+ c ← conn
183
+ (Run .liftAff (q c))
184
+ >>= case _ of
185
+ Left err → throwAt _pgError err
186
+ Right next → pure next
187
+ PgOpenTransactionF next → do
188
+ inTransaction >>= not
189
+ >>> flip when do
190
+ execute " BEGIN TRANSACTION"
191
+ modifyAt _pg _ { inTransaction = true }
192
+ pure next
193
+ PgCloseTransactionF next → do
194
+ inTransaction
195
+ >>= flip when do
196
+ execute " END TRANSACTION"
197
+ modifyAt _pg _ { inTransaction = false }
198
+ pure next
0 commit comments