purescript-postgresql-client/src/Database/PostgreSQL.purs

226 lines
5.9 KiB
Haskell
Raw Normal View History

2016-12-22 18:12:38 +00:00
module Database.PostgreSQL
( module Row
, module Value
, Database
2016-12-22 18:12:38 +00:00
, PoolConfiguration
, Pool
, Connection
2016-12-22 19:25:17 +00:00
, Query(..)
2016-12-22 18:12:38 +00:00
, newPool
, withConnection
, withTransaction
, defaultPoolConfiguration
, command
2016-12-22 18:12:38 +00:00
, execute
, query
2017-01-12 16:12:59 +00:00
, scalar
, unsafeQuery
2016-12-22 18:12:38 +00:00
) where
2017-12-04 21:43:36 +00:00
import Prelude
import Control.Monad.Error.Class (catchError, throwError)
import Data.Array (head)
2016-12-22 18:12:38 +00:00
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
2016-12-22 19:25:17 +00:00
import Data.Newtype (class Newtype)
import Data.Nullable (Nullable, toNullable)
2016-12-22 18:12:38 +00:00
import Data.Traversable (traverse)
2018-07-15 17:51:17 +00:00
import Database.PostgreSQL.Row (class FromSQLRow, class ToSQLRow, Row0(..), Row1(..), Row10(..), Row11(..), Row12(..), Row13(..), Row14(..), Row15(..), Row16(..), Row17(..), Row18(..), Row19(..), Row2(..), Row3(..), Row4(..), Row5(..), Row6(..), Row7(..), Row8(..), Row9(..), fromSQLRow, toSQLRow) as Row
import Database.PostgreSQL.Row (class FromSQLRow, class ToSQLRow, Row0(..), Row1(..), fromSQLRow, toSQLRow)
import Database.PostgreSQL.Value (class FromSQLValue)
2018-07-15 17:51:17 +00:00
import Database.PostgreSQL.Value (class FromSQLValue, class ToSQLValue, fromSQLValue, instantFromString, instantToString, null, toSQLValue, unsafeIsBuffer) as Value
import Effect (Effect)
import Effect.Aff (Aff, bracket)
import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Foreign (Foreign)
2016-12-22 18:12:38 +00:00
type Database = String
2016-12-22 18:12:38 +00:00
2016-12-24 12:38:36 +00:00
-- | PostgreSQL connection pool configuration.
2016-12-22 18:12:38 +00:00
type PoolConfiguration =
{ database :: Database
, host :: Maybe String
, idleTimeoutMillis :: Maybe Int
, max :: Maybe Int
, password :: Maybe String
, port :: Maybe Int
, user :: Maybe String
}
defaultPoolConfiguration :: Database -> PoolConfiguration
defaultPoolConfiguration database =
{ database
, host: Nothing
, idleTimeoutMillis: Nothing
, max: Nothing
, password: Nothing
, port: Nothing
, user: Nothing
2016-12-22 18:12:38 +00:00
}
2016-12-24 12:38:36 +00:00
-- | PostgreSQL connection pool.
2017-04-20 08:05:17 +00:00
foreign import data Pool :: Type
2016-12-22 18:12:38 +00:00
2016-12-24 12:38:36 +00:00
-- | PostgreSQL connection.
2017-04-20 08:05:17 +00:00
foreign import data Connection :: Type
2016-12-22 18:12:38 +00:00
2016-12-24 12:38:36 +00:00
-- | PostgreSQL query with parameter (`$1`, `$2`, …) and return types.
2016-12-22 19:25:17 +00:00
newtype Query i o = Query String
derive instance newtypeQuery :: Newtype (Query i o) _
2016-12-24 12:38:36 +00:00
-- | Create a new connection pool.
2018-07-15 17:51:17 +00:00
newPool :: PoolConfiguration -> Aff Pool
newPool cfg =
liftEffect <<< ffiNewPool $ cfg'
where
cfg' =
{ user: toNullable cfg.user
, password: toNullable cfg.password
, host: toNullable cfg.host
, port: toNullable cfg.port
, database: cfg.database
, max: toNullable cfg.max
, idleTimeoutMillis: toNullable cfg.idleTimeoutMillis
}
-- | Configuration which we actually pass to FFI.
type PoolConfiguration' =
{ user :: Nullable String
, password :: Nullable String
, host :: Nullable String
, port :: Nullable Int
, database :: String
, max :: Nullable Int
, idleTimeoutMillis :: Nullable Int
}
foreign import ffiNewPool
:: PoolConfiguration'
2018-07-15 17:51:17 +00:00
-> Effect Pool
2016-12-22 18:12:38 +00:00
2017-12-05 21:12:01 +00:00
-- | Run an action with a connection. The connection is released to the pool
-- | when the action returns.
withConnection
2018-07-15 17:51:17 +00:00
:: a
2016-12-22 18:12:38 +00:00
. Pool
2018-07-15 17:51:17 +00:00
-> (Connection -> Aff a)
-> Aff a
withConnection p k =
2017-12-04 21:43:36 +00:00
bracket
(connect p)
2018-07-15 17:51:17 +00:00
(liftEffect <<< _.done)
2017-12-04 21:43:36 +00:00
(k <<< _.connection)
2017-12-04 21:43:36 +00:00
connect
2018-07-15 17:51:17 +00:00
:: Pool
2017-12-04 21:43:36 +00:00
-> Aff
{ connection :: Connection
2018-07-15 17:51:17 +00:00
, done :: Effect Unit
2017-12-04 21:43:36 +00:00
}
2018-07-15 17:51:17 +00:00
connect = fromEffectFnAff <<< ffiConnect
2017-12-04 21:43:36 +00:00
foreign import ffiConnect
2018-07-15 17:51:17 +00:00
:: Pool
-> EffectFnAff
2017-12-04 21:43:36 +00:00
{ connection :: Connection
2018-07-15 17:51:17 +00:00
, done :: Effect Unit
2017-12-04 21:43:36 +00:00
}
2016-12-24 12:38:36 +00:00
-- | Run an action within a transaction. The transaction is committed if the
-- | action returns, and rolled back when the action throws. If you want to
-- | change the transaction mode, issue a separate `SET TRANSACTION` statement
-- | within the transaction.
2016-12-22 18:12:38 +00:00
withTransaction
2018-07-15 17:51:17 +00:00
:: a
2016-12-22 18:12:38 +00:00
. Connection
2018-07-15 17:51:17 +00:00
-> Aff a
-> Aff a
2016-12-22 18:12:38 +00:00
withTransaction conn action =
execute conn (Query "BEGIN TRANSACTION") Row0
2016-12-22 18:12:38 +00:00
*> catchError (Right <$> action) (pure <<< Left) >>= case _ of
Right a -> execute conn (Query "COMMIT TRANSACTION") Row0 $> a
Left e -> execute conn (Query "ROLLBACK TRANSACTION") Row0 *> throwError e
2016-12-22 18:12:38 +00:00
2016-12-24 12:38:36 +00:00
-- | Execute a PostgreSQL query and discard its results.
2016-12-22 18:12:38 +00:00
execute
2018-07-15 17:51:17 +00:00
:: i o
2016-12-22 18:12:38 +00:00
. (ToSQLRow i)
=> Connection
2016-12-22 19:25:17 +00:00
-> Query i o
2016-12-22 18:12:38 +00:00
-> i
2018-07-15 17:51:17 +00:00
-> Aff Unit
2016-12-22 19:25:17 +00:00
execute conn (Query sql) values =
void $ unsafeQuery conn sql (toSQLRow values)
2016-12-22 18:12:38 +00:00
2016-12-24 12:38:36 +00:00
-- | Execute a PostgreSQL query and return its results.
2016-12-22 18:12:38 +00:00
query
2018-07-15 17:51:17 +00:00
:: i o
2017-04-20 08:05:17 +00:00
. ToSQLRow i
=> FromSQLRow o
2016-12-22 18:12:38 +00:00
=> Connection
2016-12-22 19:25:17 +00:00
-> Query i o
2016-12-22 18:12:38 +00:00
-> i
2018-07-15 17:51:17 +00:00
-> Aff (Array o)
2016-12-22 19:25:17 +00:00
query conn (Query sql) values =
unsafeQuery conn sql (toSQLRow values)
>>= traverse (fromSQLRow >>> case _ of
Right row -> pure row
Left msg -> throwError (error msg))
2016-12-22 18:12:38 +00:00
-- | Execute a PostgreSQL query and return the first field of the first row in
-- | the result.
2017-01-12 16:12:59 +00:00
scalar
2018-07-15 17:51:17 +00:00
:: i o
2017-04-20 08:05:17 +00:00
. ToSQLRow i
=> FromSQLValue o
2017-01-12 16:12:59 +00:00
=> Connection
-> Query i (Row1 o)
2017-01-12 16:12:59 +00:00
-> i
2018-07-15 17:51:17 +00:00
-> Aff (Maybe o)
2017-01-12 16:12:59 +00:00
scalar conn sql values =
query conn sql values
<#> map (case _ of Row1 a -> a) <<< head
2017-01-12 16:12:59 +00:00
unsafeQuery
2018-07-15 17:51:17 +00:00
:: Connection
2017-12-04 21:43:36 +00:00
-> String
-> Array Foreign
2018-07-15 17:51:17 +00:00
-> Aff (Array (Array Foreign))
unsafeQuery c s = fromEffectFnAff <<< ffiUnsafeQuery c s
foreign import ffiUnsafeQuery
2018-07-15 17:51:17 +00:00
:: Connection
2016-12-22 18:12:38 +00:00
-> String
-> Array Foreign
2018-07-15 17:51:17 +00:00
-> EffectFnAff (Array (Array Foreign))
2016-12-22 18:12:38 +00:00
2018-10-09 06:30:12 +00:00
-- | Execute a PostgreSQL query and return its command tag value
-- | (how many rows were affected by the query). This may be useful
-- | for example with DELETE or UPDATE queries.
command
:: i
. ToSQLRow i
=> Connection
-> Query i Int
-> i
-> Aff Int
command conn (Query sql) values =
unsafeCommand conn sql (toSQLRow values)
unsafeCommand
:: Connection
-> String
-> Array Foreign
-> Aff Int
unsafeCommand c s = fromEffectFnAff <<< ffiUnsafeCommand c s
foreign import ffiUnsafeCommand
:: Connection
-> String
-> Array Foreign
-> EffectFnAff Int