20 Commits

Author SHA1 Message Date
3e9f22397d chore: prepare v1.3.0 2024-04-11 16:45:35 -05:00
d3be053a8b chore: prepare v1.3.0 2024-04-11 16:45:27 -05:00
1319a4bce7 chore: prepare v1.3.0 2024-04-11 16:45:25 -05:00
8722e69013 feat: generalize PostgresT to MonadPostgres to allow mocking 2024-04-11 16:45:13 -05:00
5dc5912933 chore: prepare v1.2.7 2024-04-11 15:55:22 -05:00
a4d4e6bd75 fix: impl unlift 2024-04-11 15:55:11 -05:00
f0d2d764fe chore: prepare v1.2.6 2024-04-11 15:38:10 -05:00
ea3ff9b003 chore: lockfile 2024-04-11 15:38:01 -05:00
4832b594dc chore: prepare v1.2.5 2024-04-11 15:37:35 -05:00
aad6544658 chore: ranges 2024-04-11 15:37:29 -05:00
89dec85a31 chore: prepare v1.2.4 2024-04-11 15:36:40 -05:00
5a99e58062 fix: impl monadrec 2024-04-11 15:36:31 -05:00
02add2653a chore: prepare v1.2.3 2024-04-05 22:15:26 -05:00
de1aaccfb6 fix: pin unresult api to rept 2024-04-05 22:15:18 -05:00
43ce1354ea chore: prepare v1.2.2 2024-04-05 22:11:51 -05:00
87614611dd fix: relax cursor constraints, fix enum test 2024-04-05 22:11:44 -05:00
2ab53c43b2 chore: prepare v1.2.1 2024-04-05 21:53:42 -05:00
500e67d793 fix: FromRow t bound on cursor 2024-04-05 21:53:37 -05:00
ca95e0aa94 chore: prepare v1.2.0 2024-04-05 21:49:36 -05:00
bc120b072c feat: UnresultT 2024-04-05 21:49:31 -05:00
10 changed files with 215 additions and 112 deletions

View File

@@ -255,59 +255,59 @@ the api of [`node-postgres`]:
- release clients with [`Pool.release`] or [`Pool.destroy`]
- release with [`Pool.end`]
[`Pool`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#t:Pool
[`Config`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#t:Config
[`Pool.make`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#v:make
[`Pool.end`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#v:end
[`Pool.connect`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#v:connect
[`Pool.destroy`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#v:destroy
[`Pool.release`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Pool#v:release
[`Pool`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#t:Pool
[`Config`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#t:Config
[`Pool.make`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#v:make
[`Pool.end`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#v:end
[`Pool.connect`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#v:connect
[`Pool.destroy`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#v:destroy
[`Pool.release`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Pool#v:release
[`Client`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#t:Client
[`Client.end`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:end
[`Client.make`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:make
[`Client.connected`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:connected
[`Client.query`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:query
[`Client.queryRaw`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:queryRaw
[`Client.exec`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Effect.Aff.Postgres.Client#v:exec
[`Client`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#t:Client
[`Client.end`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:end
[`Client.make`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:make
[`Client.connected`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:connected
[`Client.query`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:query
[`Client.queryRaw`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:queryRaw
[`Client.exec`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Effect.Aff.Postgres.Client#v:exec
[`Range`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Range#t:Range
[`Range.gt`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Range#v:gt
[`Range.gte`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Range#v:gte
[`Range.lt`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Range#v:lt
[`Range.lte`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Range#v:lte
[`Range`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Range#t:Range
[`Range.gt`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Range#v:gt
[`Range.gte`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Range#v:gte
[`Range.lt`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Range#v:lt
[`Range.lte`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Range#v:lte
[`Raw`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Raw#t:Raw
[`Null`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Raw#t:Null
[`Raw`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Raw#t:Raw
[`Null`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Raw#t:Null
[`Serialize`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres#t:Serialize
[`Deserialize`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres#t:Deserialize
[`Rep`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres#t:Rep
[`modifyPgTypes`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres#v:modifyPgTypes
[`Serialize`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres#t:Serialize
[`Deserialize`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres#t:Deserialize
[`Rep`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres#t:Rep
[`modifyPgTypes`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres#v:modifyPgTypes
[`Result`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Result#t:Result
[`FromRow`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Result#t:FromRow
[`FromRows`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Result#t:FromRows
[`Result`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Result#t:Result
[`FromRow`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Result#t:FromRow
[`FromRows`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Result#t:FromRows
[`Query`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Query#t:Query
[`AsQuery`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Query#t:AsQuery
[`Query`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Query#t:Query
[`AsQuery`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Query#t:AsQuery
[`Query.Builder`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Query.Builder#t:Builder
[`Query.Builder.param`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Query.Builder#v:param
[`Query.Builder.build`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Data.Postgres.Query.Builder#v:build
[`Query.Builder`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Query.Builder#t:Builder
[`Query.Builder.param`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Query.Builder#v:param
[`Query.Builder.build`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Data.Postgres.Query.Builder#v:build
[`MonadCursor`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#t:MonadCursor
[`MonadSession`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#t:MonadSession
[`CursorT`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#t:CursorT
[`SessionT`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#t:SessionT
[`PostgresT`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#t:PostgresT
[`cursor`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:cursor
[`session`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:session
[`transaction`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:transaction
[`runPostgres`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:runPostgres
[`query`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:query
[`exec`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:exec
[`exec_`]: https://pursuit.purescript.org///////packages/purescript-postgresql/1.1.1/docs/Control.Monad.Postgres#v:exec_
[`MonadCursor`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#t:MonadCursor
[`MonadSession`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#t:MonadSession
[`CursorT`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#t:CursorT
[`SessionT`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#t:SessionT
[`PostgresT`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#t:PostgresT
[`cursor`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:cursor
[`session`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:session
[`transaction`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:transaction
[`runPostgres`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:runPostgres
[`query`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:query
[`exec`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:exec
[`exec_`]: https://pursuit.purescript.org//////////////////packages/purescript-postgresql/1.3.0/docs/Control.Monad.Postgres#v:exec_
[`node-postgres`]: https://node-postgres.com/
[`pg-types`]: https://github.com/brianc/node-pg-types/

View File

@@ -32,9 +32,11 @@ workspace:
- record: ">=4.0.0 <5.0.0"
- simple-json: ">=9.0.0 <10.0.0"
- strings: ">=6.0.1 <7.0.0"
- tailrec: ">=6.1.0 <7.0.0"
- transformers: ">=6.0.0 <7.0.0"
- tuples: ">=7.0.0 <8.0.0"
- typelevel-prelude: ">=7.0.0 <8.0.0"
- unlift: ">=1.0.1 <2.0.0"
- unsafe-coerce: ">=6.0.0 <7.0.0"
test_dependencies:
- filterable
@@ -73,6 +75,7 @@ workspace:
- fork
- formatters
- free
- freet
- functions
- functors
- gen
@@ -86,6 +89,7 @@ workspace:
- lists
- maybe
- mmorph
- monad-control
- newtype
- node-buffer
- node-child-process
@@ -126,6 +130,7 @@ workspace:
- typelevel-prelude
- unfoldable
- unicode
- unlift
- unsafe-coerce
- variant
package_set:
@@ -923,6 +928,21 @@ packages:
- transformers
- tuples
- unsafe-coerce
freet:
type: registry
version: 7.0.0
integrity: sha256-zkL6wU4ZPq8xz1kGFxoliWqyhBksepMJTyA68VEBaJo=
dependencies:
- aff
- bifunctors
- effect
- either
- exists
- free
- prelude
- tailrec
- transformers
- tuples
functions:
type: registry
version: 6.0.0
@@ -1059,6 +1079,15 @@ packages:
- free
- functors
- transformers
monad-control:
type: registry
version: 5.0.0
integrity: sha256-bgfDW30wbIm70NR1Tvvh9P+VFQMDh1wK2sSJXCj/dZc=
dependencies:
- aff
- freet
- identity
- lists
newtype:
type: registry
version: 5.0.0
@@ -1566,6 +1595,23 @@ packages:
- foldable-traversable
- maybe
- strings
unlift:
type: registry
version: 1.0.1
integrity: sha256-nbBCVV0fZz/3UHKoW11dcTwBYmQOIgK31ht2BN47RPw=
dependencies:
- aff
- effect
- either
- freet
- identity
- lists
- maybe
- monad-control
- prelude
- st
- transformers
- tuples
unsafe-coerce:
type: registry
version: 6.0.0

View File

@@ -1,7 +1,7 @@
package:
name: postgresql
publish:
version: '1.1.1'
version: '1.3.0'
license: 'GPL-3.0-or-later'
location:
githubOwner: 'cakekindel'
@@ -42,9 +42,11 @@ package:
- record: ">=4.0.0 <5.0.0"
- simple-json: ">=9.0.0 <10.0.0"
- strings: ">=6.0.1 <7.0.0"
- tailrec: ">=6.1.0 <7.0.0"
- transformers: ">=6.0.0 <7.0.0"
- tuples: ">=7.0.0 <8.0.0"
- typelevel-prelude: ">=7.0.0 <8.0.0"
- unlift: ">=1.0.1 <2.0.0"
- unsafe-coerce: ">=6.0.0 <7.0.0"
test:
main: Test.Main

View File

@@ -7,17 +7,24 @@ import Control.Alternative (class Plus)
import Control.Monad.Error.Class (class MonadError, class MonadThrow, catchError, throwError)
import Control.Monad.Fork.Class (class MonadBracket, class MonadFork, class MonadKill, bracket, kill, never, uninterruptible)
import Control.Monad.Morph (class MFunctor, class MMonad)
import Control.Monad.Postgres.Cursor (class MonadCursor, CursorT)
import Control.Monad.Postgres.Session (class MonadSession, SessionT, exec, exec_, query)
import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local, runReaderT)
import Control.Monad.Rec.Class (class MonadRec)
import Control.Monad.Trans.Class (class MonadTrans, lift)
import Control.Parallel (class Parallel, parallel, sequential)
import Data.Newtype (class Newtype, unwrap, wrap)
import Effect.Aff (Fiber)
import Data.Postgres (RepT)
import Data.Postgres.Query (class AsQuery, asQuery)
import Data.Postgres.Raw (Raw)
import Data.Postgres.Result (class FromRow, fromRow)
import Data.Tuple.Nested ((/\))
import Effect.Aff.Class (class MonadAff, liftAff)
import Effect.Aff.Postgres.Pool (Pool)
import Effect.Aff.Postgres.Pool as Pool
import Effect.Aff.Unlift (class MonadUnliftAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Exception (Error)
import Effect.Unlift (class MonadUnliftEffect)
import Prim.Row (class Union)
-- | Monad handling pool resource acquisition & release
@@ -57,6 +64,9 @@ derive newtype instance (Bind m) => Bind (PostgresT m)
derive newtype instance (Monad m) => Monad (PostgresT m)
derive newtype instance (MonadEffect m) => MonadEffect (PostgresT m)
derive newtype instance (MonadAff m) => MonadAff (PostgresT m)
derive newtype instance (MonadUnliftEffect m) => MonadUnliftEffect (PostgresT m)
derive newtype instance (MonadUnliftAff m) => MonadUnliftAff (PostgresT m)
derive newtype instance MonadRec m => MonadRec (PostgresT m)
derive newtype instance MonadTrans (PostgresT)
derive newtype instance (MonadThrow e m) => MonadThrow e (PostgresT m)
derive newtype instance (MonadError e m) => MonadError e (PostgresT m)
@@ -86,29 +96,48 @@ instance (MonadBracket e f m, MonadAff m) => MonadSession (PostgresT m) where
exec = session <<< exec
exec_ = session <<< exec_
-- | Lifts a session to `PostgresT`, releasing the client to the pool
-- | after execution.
session :: forall e f m a. MonadBracket e f m => MonadAff m => MonadSession (SessionT m) => SessionT m a -> PostgresT m a
session m = do
pool <- ask
let
acq = liftAff $ Pool.connect pool
rel _ c = liftEffect $ Pool.release pool c
lift $ bracket acq rel (runReaderT m)
-- | Lifts a session to `PostgresT`, running the session
-- | in a transaction.
-- | Typeclass generalizing `PostgresT`. Allows for dependency-injecting different
-- | implementations of the idea of a postgres connection.
-- |
-- | If the session throws an error, the transaction will be
-- | rolled back and the error rethrown.
transaction :: forall m a. MonadBracket Error Fiber m => MonadAff m => MonadSession (SessionT m) => SessionT m a -> PostgresT m a
transaction m =
let
begin = void $ exec "begin;"
commit = m <* exec "commit;"
rollback e = exec "rollback;" *> throwError e
in
session $ begin *> catchError commit rollback
-- | - `session` - Session monad (for `PostgresT` this is `SessionT`)
-- | - `cursor` - Cursor session monad (for `PostgresT` this is `CursorT`)
-- | - `ct` - Open type parameter for cursor type. Don't pin this to a concrete type.
class (MonadSession session, MonadCursor cursor ct) <= MonadPostgres m session cursor ct | m -> ct cursor session where
-- | Run a session in `m`.
session :: session ~> m
-- | Run a session in `m`, wrapped in a transaction.
-- |
-- | If any errors are raised, the transaction is rolled back and
-- | the error rethrown.
transaction :: session ~> m
-- | `cursor`, but using a custom deserialize function for the data
-- | yielded by the cursor
cursorWith :: forall q. AsQuery q => (Array Raw -> RepT ct) -> String -> q -> cursor ~> m
instance (MonadBracket e f m, MonadAff m, MonadSession (SessionT m), MonadCursor (CursorT t (SessionT m)) t) => MonadPostgres (PostgresT m) (SessionT m) (CursorT ct (SessionT m)) ct where
session m = do
pool <- ask
let
acq = liftAff $ Pool.connect pool
rel _ c = liftEffect $ Pool.release pool c
lift $ bracket acq rel (runReaderT m)
transaction m =
let
begin = void $ exec "begin;"
commit = m <* exec "commit;"
rollback e = exec "rollback;" *> throwError e
in
session $ begin *> catchError commit rollback
cursorWith f cur q m =
transaction do
q' <- liftEffect $ asQuery q
exec_ $ "declare " <> cur <> " cursor for (" <> (unwrap q').text <> ");"
runReaderT (unwrap m) (cur /\ f)
-- | Create a server-side cursor for a query in a transaction,
-- | and execute a `CursorT` with a view to the new cursor.
cursor :: forall @cursort t session cursor q a. MonadPostgres t session cursor cursort => AsQuery q => FromRow cursort => String -> q -> cursor a -> t a
cursor = cursorWith fromRow
-- | Execute a `PostgresT` using an existing connection pool.
-- |

View File

@@ -6,23 +6,25 @@ import Control.Alt (class Alt)
import Control.Alternative (class Plus)
import Control.Monad.Error.Class (class MonadError, class MonadThrow)
import Control.Monad.Fork.Class (class MonadBracket, class MonadFork, class MonadKill, bracket, kill, never, uninterruptible)
import Control.Monad.Postgres.Base (PostgresT, transaction)
import Control.Monad.Postgres.Session (class MonadSession, SessionT, exec, exec_, query)
import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local, runReaderT)
import Control.Monad.Postgres.Session (class MonadSession, exec, exec_, query)
import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local)
import Control.Monad.Rec.Class (class MonadRec)
import Control.Monad.Trans.Class (class MonadTrans, lift)
import Control.Parallel (class Parallel, parallel, sequential)
import Data.Array as Array
import Data.Maybe (Maybe)
import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Postgres.Query (class AsQuery, asQuery)
import Data.Postgres.Result (class FromRow)
import Effect.Aff (Fiber)
import Data.Postgres (RepT, smash)
import Data.Postgres.Raw (Raw)
import Data.Traversable (traverse)
import Data.Tuple.Nested (type (/\), (/\))
import Effect.Aff.Class (class MonadAff)
import Effect.Aff.Unlift (class MonadUnliftAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Exception (Error)
import Effect.Unlift (class MonadUnliftEffect)
newtype CursorT :: forall k. Type -> (k -> Type) -> k -> Type
newtype CursorT t m a = CursorT (ReaderT String m a)
newtype CursorT t m a = CursorT (ReaderT (String /\ (Array Raw -> RepT t)) m a)
derive instance Newtype (CursorT t m a) _
derive newtype instance (Functor m) => Functor (CursorT t m)
@@ -34,6 +36,9 @@ derive newtype instance (Bind m) => Bind (CursorT t m)
derive newtype instance (Monad m) => Monad (CursorT t m)
derive newtype instance (MonadEffect m) => MonadEffect (CursorT t m)
derive newtype instance (MonadAff m) => MonadAff (CursorT t m)
derive newtype instance MonadRec m => MonadRec (CursorT t m)
derive newtype instance (MonadUnliftEffect m) => MonadUnliftEffect (CursorT t m)
derive newtype instance (MonadUnliftAff m) => MonadUnliftAff (CursorT t m)
derive newtype instance MonadTrans (CursorT t)
derive newtype instance (MonadThrow e m) => MonadThrow e (CursorT t m)
derive newtype instance (MonadError e m) => MonadError e (CursorT t m)
@@ -46,10 +51,10 @@ instance (Monad m, MonadBracket e f (ReaderT String m), MonadBracket e f m) => M
uninterruptible a = wrap $ uninterruptible $ unwrap a
never = lift $ never
instance Monad m => MonadAsk String (CursorT t m) where
instance Monad m => MonadAsk (String /\ (Array Raw -> RepT t)) (CursorT t m) where
ask = wrap ask
instance Monad m => MonadReader String (CursorT t m) where
instance Monad m => MonadReader (String /\ (Array Raw -> RepT t)) (CursorT t m) where
local f m = wrap $ local f $ unwrap m
instance (Apply m, Apply p, Parallel p m) => Parallel (CursorT t p) (CursorT t m) where
@@ -78,19 +83,21 @@ instance (Apply m, Apply p, Parallel p m) => Parallel (CursorT t p) (CursorT t m
-- | e <- fetchAll -- 15..100
-- | pure unit
-- | ```
class (MonadSession m, FromRow t) <= MonadCursor m t where
class (MonadSession m) <= MonadCursor m t where
-- | Fetch a specified number of rows from the cursor
fetch :: Int -> m (Array t)
-- | Fetch all remaining rows from the cursor
fetchAll :: m (Array t)
instance (FromRow t, MonadSession m) => MonadCursor (CursorT t m) t where
instance (MonadSession m) => MonadCursor (CursorT t m) t where
fetch n = do
cur <- ask
query $ "fetch forward " <> show n <> " from " <> cur
cur /\ f <- ask
raw :: Array (Array Raw) <- query $ "fetch forward " <> show n <> " from " <> cur
liftEffect $ smash $ traverse f raw
fetchAll = do
cur <- ask
query $ "fetch all from " <> cur
cur /\ f <- ask
raw :: Array (Array Raw) <- query $ "fetch all from " <> cur
liftEffect $ smash $ traverse f raw
instance (MonadSession m) => MonadSession (CursorT t m) where
query = lift <<< query
@@ -100,12 +107,3 @@ instance (MonadSession m) => MonadSession (CursorT t m) where
-- | Fetch the next row from the cursor
fetchOne :: forall m t. MonadCursor m t => m (Maybe t)
fetchOne = Array.head <$> fetch 1
-- | Create a server-side cursor for a query in a transaction,
-- | and execute a `CursorT` with a view to the new cursor.
cursor :: forall m @t a q. AsQuery q => MonadAff m => MonadBracket Error Fiber m => MonadSession (SessionT m) => String -> q -> CursorT t (SessionT m) a -> PostgresT m a
cursor cur q m =
transaction do
q' <- liftEffect $ asQuery q
exec_ $ "declare " <> cur <> " cursor for (" <> (unwrap q').text <> ");"
runReaderT (unwrap m) cur

View File

@@ -5,6 +5,7 @@ import Prelude
import Control.Alt ((<|>))
import Control.Monad.Error.Class (liftMaybe)
import Data.Array.NonEmpty.Internal (NonEmptyArray)
import Data.Bifunctor (lmap)
import Data.Foldable (intercalate)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep as G
@@ -17,7 +18,7 @@ import Data.Postgres.Raw (Raw)
import Data.Symbol (class IsSymbol)
import Data.Traversable (find)
import Data.Tuple (fst, snd)
import Data.Tuple.Nested (type (/\))
import Data.Tuple.Nested (type (/\), (/\))
import Foreign (ForeignError(..))
import Type.Prelude (Proxy(..), reflectSymbol)
@@ -45,19 +46,19 @@ defaultSerializeEnum :: forall @a ty. CustomEnum a ty => a -> RepT Raw
defaultSerializeEnum = serialize <<< printEnum
class GenericCustomEnum a where
genericEnumVariants' :: NonEmptyArray a
genericEnumVariants' :: NonEmptyArray (a /\ String)
genericParseEnum' :: String -> Maybe a
genericPrintEnum' :: a -> String
instance IsSymbol n => GenericCustomEnum (G.Constructor n G.NoArguments) where
genericEnumVariants' = pure (G.Constructor @n G.NoArguments)
genericEnumVariants' = pure (G.Constructor @n G.NoArguments /\ reflectSymbol (Proxy @n))
genericParseEnum' s
| s == reflectSymbol (Proxy @n) = Just (G.Constructor @n G.NoArguments)
| otherwise = Nothing
genericPrintEnum' _ = reflectSymbol (Proxy @n)
instance (GenericCustomEnum a, GenericCustomEnum b) => GenericCustomEnum (G.Sum a b) where
genericEnumVariants' = (G.Inl <$> genericEnumVariants' @a) <> (G.Inr <$> genericEnumVariants' @b)
genericEnumVariants' = (lmap G.Inl <$> genericEnumVariants' @a) <> (lmap G.Inr <$> genericEnumVariants' @b)
genericParseEnum' s = (G.Inl <$> genericParseEnum' @a s) <|> (G.Inr <$> genericParseEnum' @b s)
genericPrintEnum' (G.Inl a) = genericPrintEnum' a
genericPrintEnum' (G.Inr a) = genericPrintEnum' a
@@ -65,8 +66,8 @@ instance (GenericCustomEnum a, GenericCustomEnum b) => GenericCustomEnum (G.Sum
enumPrintExpr :: forall @a ty. CustomEnum a ty => a -> Maybe String
enumPrintExpr = Just <<< (\s -> quoted s <> " :: " <> typeName @a) <<< printEnum
genericEnumVariants :: forall a g. Generic a g => GenericCustomEnum g => NonEmptyArray a
genericEnumVariants = G.to <$> genericEnumVariants'
genericEnumVariants :: forall a g. Generic a g => GenericCustomEnum g => NonEmptyArray (a /\ String)
genericEnumVariants = lmap G.to <$> genericEnumVariants'
genericParseEnum :: forall a g. Generic a g => GenericCustomEnum g => String -> Maybe a
genericParseEnum = map G.to <<< genericParseEnum'

View File

@@ -8,7 +8,7 @@ import Data.Int as Int
import Data.Maybe (Maybe)
import Data.Nullable (Nullable)
import Data.Nullable as Nullable
import Data.Postgres (class Rep, RepT, deserialize)
import Data.Postgres (class Deserialize, class Rep, RepT, deserialize)
import Data.Postgres.Raw (Raw)
import Data.Traversable (traverse)
import Data.Tuple (Tuple)
@@ -79,7 +79,7 @@ class FromRow (a :: Type) where
-- | Performs the conversion
fromRow :: Array Raw -> RepT a
instance (Rep a, FromRow b) => FromRow (a /\ b) where
instance (Deserialize a, FromRow b) => FromRow (a /\ b) where
minColumnCount _ = minColumnCount (Proxy @b) + 1
fromRow r =
let
@@ -97,7 +97,7 @@ else instance FromRow (Array Raw) where
else instance FromRow Unit where
minColumnCount _ = 0
fromRow _ = pure unit
else instance Rep a => FromRow a where
else instance Deserialize a => FromRow a where
minColumnCount _ = 1
fromRow r =
let

View File

@@ -0,0 +1,27 @@
module Data.Postgres.Unresult where
import Prelude
import Control.Monad.Error.Class (liftMaybe)
import Control.Monad.State (StateT, runStateT, state)
import Data.Array as Array
import Data.Postgres (class Deserialize, RepT, deserialize, smash)
import Data.Postgres.Raw (Raw)
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\))
import Effect.Class (liftEffect)
import Foreign (ForeignError(..))
-- | Monad used to incrementally deserialize columns from a row
type Unresult a = StateT { ix :: Int, row :: Array Raw } RepT a
-- | Run an `UnresultT`
unresult :: forall a. Unresult a -> Array Raw -> RepT a
unresult m row = fst <$> runStateT m { ix: 0, row }
-- | Take the next column from the row, unmarshalling into `a`
take :: forall a. Deserialize a => Unresult a
take = do
raw <- state (\r -> Array.index r.row r.ix /\ r { ix = r.ix + 1 })
raw' <- liftMaybe (pure $ ForeignError $ "Ran out of columns!") raw
liftEffect $ smash $ deserialize raw'

View File

@@ -41,7 +41,7 @@ derive newtype instance ReadForeign a => ReadForeign (JSON a)
foreign import modifyPgTypes :: Effect Unit
-- | The serialization & deserialization monad.
type RepT a = ExceptT (NonEmptyList ForeignError) Effect a
type RepT = ExceptT (NonEmptyList ForeignError) Effect
-- | Flatten to an Effect, `show`ing errors
smash :: forall a. RepT a -> Effect a

View File

@@ -10,7 +10,7 @@ import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..))
import Data.Newtype (unwrap)
import Data.Postgres (class Deserialize, class Serialize, deserialize, serialize, smash)
import Data.Postgres.Custom.Enum (class CustomEnum, create, enumDeserialize, enumPrintExpr, enumSerialize, genericEnumVariants, genericParseEnum, genericPrintEnum, parseEnum, printEnum)
import Data.Postgres.Custom.Enum (class CustomEnum, create, defaultDeserializeEnum, defaultSerializeEnum, enumPrintExpr, genericEnumVariants, genericParseEnum, genericPrintEnum, parseEnum, printEnum)
import Data.Show.Generic (genericShow)
import Effect.Class (liftEffect)
import Test.Spec (Spec, describe, it)
@@ -25,10 +25,10 @@ instance Show Enum1 where
show = genericShow
instance Serialize Enum1 where
serialize a = enumSerialize a
serialize a = defaultSerializeEnum a
instance Deserialize Enum1 where
deserialize a = enumDeserialize a
deserialize a = defaultDeserializeEnum a
instance CustomEnum Enum1 "enum_1" where
printEnum = genericPrintEnum
@@ -43,10 +43,10 @@ instance Show Enum2 where
show = genericShow
instance Serialize Enum2 where
serialize a = enumSerialize a
serialize a = defaultSerializeEnum a
instance Deserialize Enum2 where
deserialize a = enumDeserialize a
deserialize a = defaultDeserializeEnum a
instance CustomEnum Enum2 "enum_2" where
printEnum a = genericPrintEnum a
@@ -61,10 +61,10 @@ instance Show Enum5 where
show = genericShow
instance Serialize Enum5 where
serialize a = enumSerialize a
serialize a = defaultSerializeEnum a
instance Deserialize Enum5 where
deserialize a = enumDeserialize a
deserialize a = defaultDeserializeEnum a
instance CustomEnum Enum5 "enum_5" where
printEnum a = genericPrintEnum a