5 Commits

Author SHA1 Message Date
3be968da3a chore: prepare v1.4.0 2024-04-29 09:33:44 -05:00
20a0a6de31 chore: prepare v1.4.0 2024-04-29 09:32:23 -05:00
036a7b5de9 chore: prepare v1.4.0 2024-04-29 09:32:19 -05:00
639bee96d0 chore: prepare v 2024-04-29 09:32:05 -05:00
24b2156524 feat: MOVE support for cursors, RowsAffected 2024-04-29 09:31:49 -05:00
5 changed files with 99 additions and 59 deletions

100
README.md
View File

@@ -116,6 +116,8 @@ which is implemented for:
- `Array a` where `a` is [`FromRow`]
- `Maybe a` where `a` is [`FromRow`] (equivalent to `Array.head <<< fromRows`)
- `a` where `a` is [`FromRow`] (throws if 0 rows yielded)
- `RowsAffected`
- Extracts the number of rows processed by the last command in the query (ex. `INSERT INTO foo (bar) VALUES ('a'), ('b')` -> `INSERT 2` -> `RowsAffected 2`)
### Data - Ranges
Postgres ranges are represented with [`Range`].
@@ -223,9 +225,11 @@ Execute [`CursorT`] monads with [`cursor`]:
dbMain :: PostgresT Aff Int
dbMain =
cursor @(Int /\ String) "people_cursor" "select id, name from persons" do
fetchOne -- Just (1 /\ "Henry")
fetchAll -- [2 /\ "Sarah"]
fetchOne -- Nothing
a <- fetchOne -- Just (1 /\ "Henry")
b <- fetchOne -- Just (2 /\ "Sarah")
void $ move (MoveRelative -2)
c <- fetchAll -- [1 /\ "Henry", 2 /\ "Sarah"]
d <- fetchOne -- Nothing
```
### Monads - `SessionT`
@@ -255,59 +259,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.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
[`Pool`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#t:Pool
[`Config`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#t:Config
[`Pool.make`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#v:make
[`Pool.end`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#v:end
[`Pool.connect`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#v:connect
[`Pool.destroy`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#v:destroy
[`Pool.release`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Pool#v:release
[`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
[`Client`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#t:Client
[`Client.end`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:end
[`Client.make`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:make
[`Client.connected`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:connected
[`Client.query`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:query
[`Client.queryRaw`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:queryRaw
[`Client.exec`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Effect.Aff.Postgres.Client#v:exec
[`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
[`Range`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Range#t:Range
[`Range.gt`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Range#v:gt
[`Range.gte`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Range#v:gte
[`Range.lt`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Range#v:lt
[`Range.lte`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Range#v:lte
[`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
[`Raw`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Raw#t:Raw
[`Null`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Raw#t:Null
[`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
[`Serialize`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres#t:Serialize
[`Deserialize`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres#t:Deserialize
[`Rep`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres#t:Rep
[`modifyPgTypes`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres#v:modifyPgTypes
[`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
[`Result`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Result#t:Result
[`FromRow`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Result#t:FromRow
[`FromRows`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Result#t:FromRows
[`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`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Query#t:Query
[`AsQuery`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Query#t:AsQuery
[`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
[`Query.Builder`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Query.Builder#t:Builder
[`Query.Builder.param`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Query.Builder#v:param
[`Query.Builder.build`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Data.Postgres.Query.Builder#v:build
[`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_
[`MonadCursor`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#t:MonadCursor
[`MonadSession`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#t:MonadSession
[`CursorT`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#t:CursorT
[`SessionT`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#t:SessionT
[`PostgresT`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#t:PostgresT
[`cursor`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:cursor
[`session`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:session
[`transaction`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:transaction
[`runPostgres`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:runPostgres
[`query`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:query
[`exec`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:exec
[`exec_`]: https://pursuit.purescript.org//////////////////////packages/purescript-postgresql/1.4.0/Control.Monad.Postgres#v:exec_
[`node-postgres`]: https://node-postgres.com/
[`pg-types`]: https://github.com/brianc/node-pg-types/

View File

@@ -1,7 +1,7 @@
package:
name: postgresql
publish:
version: '1.3.0'
version: '1.4.0'
license: 'GPL-3.0-or-later'
location:
githubOwner: 'cakekindel'

View File

@@ -16,6 +16,7 @@ import Data.Maybe (Maybe)
import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Postgres (RepT, smash)
import Data.Postgres.Raw (Raw)
import Data.Postgres.Result (RowsAffected(..))
import Data.Traversable (traverse)
import Data.Tuple.Nested (type (/\), (/\))
import Effect.Aff.Class (class MonadAff)
@@ -23,6 +24,12 @@ import Effect.Aff.Unlift (class MonadUnliftAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Unlift (class MonadUnliftEffect)
data Move
-- | `MOVE RELATIVE`
= MoveRelative Int
-- | `MOVE ABSOLUTE`
| MoveTo Int
newtype CursorT :: forall k. Type -> (k -> Type) -> k -> Type
newtype CursorT t m a = CursorT (ReaderT (String /\ (Array Raw -> RepT t)) m a)
@@ -88,6 +95,9 @@ class (MonadSession m) <= MonadCursor m t where
fetch :: Int -> m (Array t)
-- | Fetch all remaining rows from the cursor
fetchAll :: m (Array t)
-- | Change the cursor's position without fetching any data,
-- | returning the number of rows skipped.
move :: Move -> m Int
instance (MonadSession m) => MonadCursor (CursorT t m) t where
fetch n = do
@@ -98,6 +108,14 @@ instance (MonadSession m) => MonadCursor (CursorT t m) t where
cur /\ f <- ask
raw :: Array (Array Raw) <- query $ "fetch all from " <> cur
liftEffect $ smash $ traverse f raw
move (MoveTo n) = do
cur /\ _ <- ask
RowsAffected n' <- query $ ("move absolute $1 from " <> cur) /\ n
pure n'
move (MoveRelative n) = do
cur /\ _ <- ask
RowsAffected n' <- query $ ("move relative $1 from " <> cur) /\ n
pure n'
instance (MonadSession m) => MonadSession (CursorT t m) where
query = lift <<< query

View File

@@ -4,11 +4,13 @@ import Prelude
import Control.Monad.Error.Class (liftMaybe, throwError)
import Data.Array as Array
import Data.Generic.Rep (class Generic)
import Data.Int as Int
import Data.Maybe (Maybe)
import Data.Newtype (class Newtype)
import Data.Nullable (Nullable)
import Data.Nullable as Nullable
import Data.Postgres (class Deserialize, class Rep, RepT, deserialize)
import Data.Postgres (class Deserialize, RepT, deserialize)
import Data.Postgres.Raw (Raw)
import Data.Traversable (traverse)
import Data.Tuple (Tuple)
@@ -27,19 +29,29 @@ foreign import data Result :: Type
rowsAffected :: Result -> Maybe Int
rowsAffected = Int.fromNumber <=< Nullable.toMaybe <<< __rowsAffected
class FromRows a where
fromRows :: Array (Array Raw) -> RepT a
newtype RowsAffected = RowsAffected Int
instance (FromRow a) => FromRows (Array a) where
fromRows = traverse fromRow
derive instance Newtype RowsAffected _
derive instance Generic RowsAffected _
derive newtype instance Eq RowsAffected
derive newtype instance Ord RowsAffected
derive newtype instance Show RowsAffected
class FromRows a where
fromRows :: RowsAffected -> Array (Array Raw) -> RepT a
instance FromRows RowsAffected where
fromRows a _ = pure a
else instance (FromRow a) => FromRows (Array a) where
fromRows _ = traverse fromRow
else instance (FromRow a) => FromRows (Maybe a) where
fromRows = map Array.head <<< traverse fromRow
fromRows _ = map Array.head <<< traverse fromRow
else instance (FromRow a) => FromRows a where
fromRows =
fromRows a =
let
e = pure $ ForeignError $ "Expected at least 1 row"
in
liftMaybe e <=< fromRows @(Maybe a)
liftMaybe e <=< fromRows @(Maybe a) a
-- | Can be unmarshalled from a queried row
-- |

View File

@@ -6,14 +6,15 @@ import Control.Promise (Promise)
import Control.Promise as Promise
import Data.Functor (voidRight)
import Data.Maybe (fromMaybe)
import Data.Newtype (wrap)
import Data.Postgres (smash)
import Data.Postgres.Query (class AsQuery, QueryRaw, asQuery, __queryToRaw)
import Data.Postgres.Result (class FromRows, Result, fromRows, rows, rowsAffected)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Effect.Postgres.Client (Client, Config, make)
import Effect.Postgres.Client (Client, ClientConfigRaw, Config, Notification, NotificationRaw, __make, __uncfg, endE, errorE, make, noticeE, notificationE) as X
import Effect.Postgres.Client (Client, Config, make)
import Prim.Row (class Union)
-- | Create a client and immediately connect it to the database
@@ -56,7 +57,12 @@ exec q = map (fromMaybe 0 <<< rowsAffected) <<< queryRaw q
-- |
-- | <https://node-postgres.com/apis/client#clientquery>
query :: forall q r. AsQuery q => FromRows r => q -> Client -> Aff r
query q = (liftEffect <<< smash <<< fromRows) <=< map rows <<< queryRaw q
query q c = do
raw <- queryRaw q c
let
affected = rowsAffected raw
rows' = rows raw
liftEffect $ smash $ fromRows (wrap $ fromMaybe 0 affected) rows'
-- | FFI binding to `Client#connect`
foreign import __connect :: Client -> Effect (Promise Unit)